From da052bdb0aaee266650d8f4ec070c51f42a6cd00 Mon Sep 17 00:00:00 2001 From: Adam Chelminski Date: Thu, 23 Jun 2022 15:50:35 +0200 Subject: [PATCH 001/362] Make encodeStateAsUpdate deterministic --- src/utils/DeleteSet.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index d396c97bf..6cde06553 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -219,17 +219,21 @@ export const createDeleteSetFromStructStore = ss => { */ export const writeDeleteSet = (encoder, ds) => { encoding.writeVarUint(encoder.restEncoder, ds.clients.size) - ds.clients.forEach((dsitems, client) => { - encoder.resetDsCurVal() - encoding.writeVarUint(encoder.restEncoder, client) - const len = dsitems.length - encoding.writeVarUint(encoder.restEncoder, len) - for (let i = 0; i < len; i++) { - const item = dsitems[i] - encoder.writeDsClock(item.clock) - encoder.writeDsLen(item.len) - } - }) + + // Ensure that the delete set is written in a deterministic order + Array.from(ds.clients.entries()) + .sort((clientA, clientB) => clientB[0] - clientA[0]) + .forEach(([client, dsitems]) => { + encoder.resetDsCurVal() + encoding.writeVarUint(encoder.restEncoder, client) + const len = dsitems.length + encoding.writeVarUint(encoder.restEncoder, len) + for (let i = 0; i < len; i++) { + const item = dsitems[i] + encoder.writeDsClock(item.clock) + encoder.writeDsLen(item.len) + } + }) } /** From 6b7b3136e01314b98dcfec58fd139402a6229325 Mon Sep 17 00:00:00 2001 From: Adam Chelminski Date: Thu, 23 Jun 2022 16:01:29 +0200 Subject: [PATCH 002/362] delete set encoding should be in descending order --- src/utils/DeleteSet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 6cde06553..32fd089b4 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -222,7 +222,7 @@ export const writeDeleteSet = (encoder, ds) => { // Ensure that the delete set is written in a deterministic order Array.from(ds.clients.entries()) - .sort((clientA, clientB) => clientB[0] - clientA[0]) + .sort((clientA, clientB) => clientA[0] - clientB[0]) .forEach(([client, dsitems]) => { encoder.resetDsCurVal() encoding.writeVarUint(encoder.restEncoder, client) From e0e5f8d2ea749d4ef1820e4ea6608ee44805cc33 Mon Sep 17 00:00:00 2001 From: Aart Rost Date: Wed, 10 Aug 2022 14:07:40 -0700 Subject: [PATCH 003/362] Allow updating captureTimeout on UndoManager instances Used to pause the undoManager by toggling the timeout with `yUndoManager.captureTimeout = Number.MAX_VALUE` --- src/utils/UndoManager.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index c53924a18..99efb94a1 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -192,6 +192,7 @@ export class UndoManager extends Observable { this.doc = doc this.lastChange = 0 this.ignoreRemoteMapChanges = ignoreRemoteMapChanges + this.captureTimeout = captureTimeout /** * @param {Transaction} transaction */ @@ -223,7 +224,7 @@ export class UndoManager extends Observable { }) const now = time.getUnixTime() let didAdd = false - if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) { + if (now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) { // append change to last stack op const lastOp = stack[stack.length - 1] lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]) From 3d7ef7e28b799649cb09dcf8f61564c56362ef0c Mon Sep 17 00:00:00 2001 From: MaxNoetzold <69591795+MaxNoetzold@users.noreply.github.com> Date: Sun, 18 Sep 2022 14:17:52 +0200 Subject: [PATCH 004/362] add y-mongodb-provider to provider list in README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bcb18d1dc..aaa06e986 100644 --- a/README.md +++ b/README.md @@ -137,6 +137,10 @@ Use Matrix as transport and storage of Yjs updates, so you can focus building your client app and Matrix can provide powerful features like Authentication, Authorization, Federation, hosting (self-hosting or SaaS) and even End-to-End Encryption (E2EE). + +
y-mongodb-provider
+
+Adds persistent storage to a server with MongoDB. Can be used with the y-websocket provider.
From f1ad5686c11a11f0bf5ec37c4eb6bc2c79600e3c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 2 Oct 2022 17:10:14 +0200 Subject: [PATCH 005/362] Add Hyperquery to -Who Is Using Yjs- --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aaa06e986..cd4d7ae5e 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Slidebeamer](https://slidebeamer.com/) Presentation app. * [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys. * [Skiff](https://skiff.org/) Private, decentralized workspace. +* [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. ## Table of Contents From 7395229086d9d4db06ebd5936bdd326dbe346834 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Oct 2022 16:45:30 +0200 Subject: [PATCH 006/362] Port test from @PatrickShaw #432. Allow infinite captureTimeout in UndoManager #431. Closes #432 --- src/utils/UndoManager.js | 2 +- tests/undo-redo.tests.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 894a532f0..5293cb963 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -224,7 +224,7 @@ export class UndoManager extends Observable { }) const now = time.getUnixTime() let didAdd = false - if (now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) { + if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) { // append change to last stack op const lastOp = stack[stack.length - 1] lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 3b3794f0c..3ee65d18b 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -3,6 +3,19 @@ import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint import * as Y from '../src/index.js' import * as t from 'lib0/testing' +/** + * @param {t.TestCase} tc + */ +export const testInfiniteCaptureTimeout = tc => { + const { array0 } = init(tc, { users: 3 }) + const undoManager = new Y.UndoManager(array0, { captureTimeout: Number.MAX_VALUE }) + array0.push([1, 2, 3]) + undoManager.stopCapturing() + array0.push([4, 5, 6]) + undoManager.undo() + t.compare(array0.toArray(), [1, 2, 3]) +} + /** * @param {t.TestCase} tc */ From 12a9134b095923e8da3ddf2cd471e7094e541525 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Oct 2022 16:51:07 +0200 Subject: [PATCH 007/362] lint --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cd4d7ae5e..14779c6c3 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 ## Who is using Yjs -* [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 +* [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source + knowledge base. 🏅 * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: * [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and community. :star: @@ -53,7 +54,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Slidebeamer](https://slidebeamer.com/) Presentation app. * [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys. * [Skiff](https://skiff.org/) Private, decentralized workspace. -* [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. +* [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for + sharing analyses, documentation, spreadsheets, and dashboards. ## Table of Contents @@ -141,7 +143,8 @@ Encryption (E2EE).
y-mongodb-provider
-Adds persistent storage to a server with MongoDB. Can be used with the y-websocket provider. +Adds persistent storage to a server with MongoDB. Can be used with the +y-websocket provider.
From 6208b8287212f3bfbd104efaa76795624e0200cb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Oct 2022 16:52:38 +0200 Subject: [PATCH 008/362] 13.5.42 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69ea9528f..9b522911a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.41", + "version": "13.5.42", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.41", + "version": "13.5.42", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index 83a3db73f..93aac6b05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.41", + "version": "13.5.42", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 7405057037e8653301093fbc3b86aec3aa06ec89 Mon Sep 17 00:00:00 2001 From: Viktor Qvarfordt Date: Sun, 23 Oct 2022 21:34:43 +0200 Subject: [PATCH 009/362] Add Sana to -Who Is Using Yjs- --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 14779c6c3..5dfa261c1 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 ## Who is using Yjs +* [Sana](https://sanalabs.com/) A learning platform with collaborative text editing powered by Yjs. * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: From d7751c16fd660136eb4dde33144e766eafbcdf24 Mon Sep 17 00:00:00 2001 From: Joakim Date: Sat, 5 Nov 2022 21:15:33 +0100 Subject: [PATCH 010/362] Add missing word --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5dfa261c1..d057b10f5 100644 --- a/README.md +++ b/README.md @@ -1060,16 +1060,17 @@ More information about the specific implementation is available in [INTERNALS.md](./INTERNALS.md) and in [this walkthrough of the Yjs codebase](https://youtu.be/0l5XgnQ6rB4). -CRDTs that suitable for shared text editing suffer from the fact that they only grow -in size. There are CRDTs that do not grow in size, but they do not have the -characteristics that are benificial for shared text editing (like intention -preservation). Yjs implements many improvements to the original algorithm that -diminish the trade-off that the document only grows in size. We can't garbage -collect deleted structs (tombstones) while ensuring a unique order of the -structs. But we can 1. merge preceeding structs into a single struct to reduce -the amount of meta information, 2. we can delete content from the struct if it -is deleted, and 3. we can garbage collect tombstones if we don't care about the -order of the structs anymore (e.g. if the parent was deleted). +CRDTs that are suitable for shared text editing suffer from the fact that they +only grow in size. There are CRDTs that do not grow in size, but they do not +have the characteristics that are benificial for shared text editing (like +intention preservation). Yjs implements many improvements to the original +algorithm that diminish the trade-off that the document only grows in size. We +can't garbage collect deleted structs (tombstones) while ensuring a unique +order of the structs. But we can 1. merge preceeding structs into a single +struct to reduce the amount of meta information, 2. we can delete content from +the struct if it is deleted, and 3. we can garbage collect tombstones if we +don't care about the order of the structs anymore (e.g. if the parent was +deleted). **Examples:** From cac9407185ab73aa6d2b1288045c46fae382e067 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Nov 2022 09:23:35 +0100 Subject: [PATCH 011/362] remove snapshot param in yxml.getAttributes --- src/types/YXmlElement.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 464951b4f..59c51cffb 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -176,12 +176,11 @@ 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. * * @public */ - getAttributes (snapshot) { + getAttributes () { return typeMapGetAll(this) } From 3ece681758cb7864264c23eb6a13f9ef68de3609 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Nov 2022 12:09:19 +0100 Subject: [PATCH 012/362] allow transactions within event handlers having different origins --- src/types/YText.js | 2 +- src/utils/Transaction.js | 25 ++++++++++++++----------- tests/doc.tests.js | 25 +++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 896fcdd43..887eb6cb3 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1064,7 +1064,7 @@ export class YText extends AbstractType { n = n.right } packStr() - }, splitSnapshotAffectedStructs) + }, 'cleanup') return ops } diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index a933056c9..f6a1179a6 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -251,7 +251,6 @@ const cleanupTransactions = (transactionCleanups, i) => { try { sortAndMergeDeleteSet(ds) transaction.afterState = getStateVector(transaction.doc.store) - doc._transaction = null doc.emit('beforeObserverCalls', [transaction, doc]) /** * An array of event callbacks. @@ -398,16 +397,20 @@ export const transact = (doc, f, origin = null, local = true) => { try { f(doc._transaction) } finally { - if (initialCall && transactionCleanups[0] === doc._transaction) { - // The first transaction ended, now process observer calls. - // Observer call may create new transactions for which we need to call the observers and do cleanup. - // We don't want to nest these calls, so we execute these calls one after - // another. - // Also we need to ensure that all cleanups are called, even if the - // observes throw errors. - // This file is full of hacky try {} finally {} blocks to ensure that an - // event can throw errors and also that the cleanup is called. - cleanupTransactions(transactionCleanups, 0) + if (initialCall) { + const finishCleanup = doc._transaction === transactionCleanups[0] + doc._transaction = null + if (finishCleanup) { + // The first transaction ended, now process observer calls. + // Observer call may create new transactions for which we need to call the observers and do cleanup. + // We don't want to nest these calls, so we execute these calls one after + // another. + // Also we need to ensure that all cleanups are called, even if the + // observes throw errors. + // This file is full of hacky try {} finally {} blocks to ensure that an + // event can throw errors and also that the cleanup is called. + cleanupTransactions(transactionCleanups, 0) + } } } } diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 994ecaebd..724958728 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -2,6 +2,31 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' +/** + * @param {t.TestCase} _tc + */ +export const testOriginInTransaction = _tc => { + const doc = new Y.Doc() + const ytext = doc.getText() + /** + * @type {Array} + */ + const origins = [] + doc.on('afterTransaction', (tr) => { + origins.push(tr.origin) + if (origins.length <= 1) { + ytext.toDelta() + doc.transact(() => { + ytext.insert(0, 'a') + }, 'nested') + } + }) + doc.transact(() => { + ytext.insert(0, '0') + }, 'first') + t.compareArrays(origins, ['first', 'cleanup', 'nested']) +} + /** * Client id should be changed when an instance receives updates from another client using the same client id. * From 0ef5bd42fe5a23599b2d9fe43a799c9f2bf9ed6c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Nov 2022 12:26:55 +0100 Subject: [PATCH 013/362] lint readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d057b10f5..75162d0b5 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 ## Who is using Yjs -* [Sana](https://sanalabs.com/) A learning platform with collaborative text editing powered by Yjs. +* [Sana](https://sanalabs.com/) A learning platform with collaborative text + editing powered by Yjs. * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: From afc6728c9ea2560e6af04b963eec9020c5d02f34 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Nov 2022 12:28:28 +0100 Subject: [PATCH 014/362] 13.5.43 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b522911a..17379eee7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.42", + "version": "13.5.43", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.42", + "version": "13.5.43", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index 93aac6b05..7182543d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.42", + "version": "13.5.43", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From ab978b20034db0a1bb5d06d12aa16a90cb2b308b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 1 Jan 2023 18:23:21 +0100 Subject: [PATCH 015/362] add exports.module field - #438 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7182543d5..873dac2ed 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "exports": { ".": { "types": "./dist/src/index.d.ts", + "module": "./dist/yjs.mjs", "import": "./dist/yjs.mjs", "require": "./dist/yjs.cjs" }, From 31b4ab8d0cd106dcc5d08b10d9b7f2dfdaedd45d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 1 Jan 2023 18:25:03 +0100 Subject: [PATCH 016/362] 13.5.44 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17379eee7..5acfdc03c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.43", + "version": "13.5.44", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.43", + "version": "13.5.44", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index 873dac2ed..5989d8795 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.43", + "version": "13.5.44", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 1130abe05bf4978108b588f735edb3cd224f6a3a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 18 Jan 2023 12:20:52 +0100 Subject: [PATCH 017/362] add POXi as a user --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75162d0b5..ddca18e69 100644 --- a/README.md +++ b/README.md @@ -34,11 +34,11 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 ## Who is using Yjs -* [Sana](https://sanalabs.com/) A learning platform with collaborative text - editing powered by Yjs. * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: +* [Sana](https://sanalabs.com/) A learning platform with collaborative text + editing powered by Yjs. * [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and community. :star: * [Room.sh](https://room.sh/) A meeting application with integrated @@ -58,6 +58,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Skiff](https://skiff.org/) Private, decentralized workspace. * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. +* [POXi](poxi.page) is a real-time collaborative application aiming to be a creative space where users can create their own "digital home". ## Table of Contents From ab60cd1ff836649aba8d1b661a4e7b4ea1e5cc9a Mon Sep 17 00:00:00 2001 From: Mael Date: Thu, 19 Jan 2023 15:06:20 +0100 Subject: [PATCH 018/362] New user --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ddca18e69..9802a1df7 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. * [POXi](poxi.page) is a real-time collaborative application aiming to be a creative space where users can create their own "digital home". - +* [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon footprint calculator has a group P2P mode based on yjs + ## Table of Contents * [Overview](#Overview) From f2158664298424c5d309ae87a2c0671399b96968 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 19 Jan 2023 15:24:53 +0100 Subject: [PATCH 019/362] remove poxi on request.. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 9802a1df7..140cde7fe 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,6 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Skiff](https://skiff.org/) Private, decentralized workspace. * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. -* [POXi](poxi.page) is a real-time collaborative application aiming to be a creative space where users can create their own "digital home". * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon footprint calculator has a group P2P mode based on yjs ## Table of Contents From d29de75f859621c45b9e6e520d1a9a1338b421d8 Mon Sep 17 00:00:00 2001 From: Neftaly Hernandez Date: Mon, 23 Jan 2023 06:41:57 +0000 Subject: [PATCH 020/362] Add test for Y.Array.from --- tests/y-array.tests.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 8cdaac5b6..941598ac9 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -32,6 +32,17 @@ export const testSlice = tc => { t.compareArrays(arr.slice(0, 2), [0, 1]) } +/** + * @param {t.TestCase} tc + */ +export const testArrayFrom = tc => { + const doc1 = new Y.Doc() + const db1 = doc1.getMap('root') + const nestedArray1 = Y.Array.from([0, 1, 2]) + db1.set('array', nestedArray1) + t.compare(nestedArray1.toArray(), [0, 1, 2]) +} + /** * Debugging yjs#297 - a critical bug connected to the search-marker approach * From 7f6c12a541d4df9f26e7bf325d55d6d34655db3b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 31 Jan 2023 12:16:03 +0100 Subject: [PATCH 021/362] bump typescript and fix type issues --- README.md | 5 +- package-lock.json | 1472 ++++++++++++++++++++------------ package.json | 2 +- src/types/AbstractType.js | 8 +- src/types/YArray.js | 21 +- src/types/YMap.js | 16 +- src/types/YText.js | 11 +- src/types/YXmlElement.js | 2 +- src/types/YXmlFragment.js | 8 +- src/utils/Doc.js | 2 +- src/utils/PermanentUserData.js | 4 +- tests/doc.tests.js | 32 +- tests/y-map.tests.js | 4 +- 13 files changed, 972 insertions(+), 615 deletions(-) diff --git a/README.md b/README.md index 140cde7fe..916c0c513 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,9 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Skiff](https://skiff.org/) Private, decentralized workspace. * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. -* [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon footprint calculator has a group P2P mode based on yjs - +* [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon + footprint calculator has a group P2P mode based on yjs + ## Table of Contents * [Overview](#Overview) diff --git a/package-lock.json b/package-lock.json index 5acfdc03c..c3f98add1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "rollup": "^2.60.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^4.4.4", + "typescript": "^4.9.5", "y-protocols": "^1.0.5" }, "funding": { @@ -30,33 +30,33 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.18.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -65,9 +65,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", - "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", + "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -202,7 +202,7 @@ "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "node_modules/@types/linkify-it": { @@ -228,9 +228,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", - "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "node_modules/@types/resolve": { @@ -280,9 +280,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -316,15 +316,15 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "is-string": "^1.0.7" }, "engines": { @@ -335,14 +335,14 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", - "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -353,14 +353,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -388,6 +388,18 @@ "lodash": "^4.17.14" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -397,7 +409,7 @@ "node_modules/basic-auth": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "integrity": "sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA==", "dev": true, "engines": { "node": ">= 0.6" @@ -412,7 +424,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, "node_modules/brace-expansion": { @@ -426,9 +438,9 @@ } }, "node_modules/builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, "engines": { "node": ">=6" @@ -488,7 +500,7 @@ "node_modules/chalk/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" @@ -509,7 +521,7 @@ "node_modules/cheerio": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", "dev": true, "dependencies": { "css-select": "~1.2.0", @@ -551,7 +563,7 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/colors": { @@ -566,7 +578,7 @@ "node_modules/commander": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", + "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", "dev": true, "engines": { "node": ">= 0.6.x" @@ -575,13 +587,13 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/concurrently": { @@ -611,7 +623,7 @@ "node_modules/corser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, "engines": { "node": ">= 0.4.0" @@ -634,7 +646,7 @@ "node_modules/css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", "dev": true, "dependencies": { "boolbase": "~1.0.0", @@ -684,9 +696,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", "dev": true, "engines": { "node": ">=0.10.0" @@ -754,7 +766,7 @@ "node_modules/domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", "dev": true, "dependencies": { "dom-serializer": "0", @@ -814,31 +826,44 @@ } }, "node_modules/es-abstract": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "dependencies": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" }, "engines": { "node": ">= 0.4" @@ -847,6 +872,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-shim-unscopables": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", @@ -876,7 +915,7 @@ "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "engines": { "node": ">=0.8.0" @@ -987,26 +1026,31 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "dev": true, "dependencies": { "debug": "^3.2.7", - "resolve": "^1.20.0" + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" } }, "node_modules/eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", "dev": true, "dependencies": { - "debug": "^3.2.7", - "find-up": "^2.1.0" + "debug": "^3.2.7" }, "engines": { "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, "node_modules/eslint-plugin-es": { @@ -1081,7 +1125,7 @@ "node_modules/eslint-plugin-import/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/eslint-plugin-node": { @@ -1165,13 +1209,17 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "dev": true, "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1323,9 +1371,9 @@ "dev": true }, "node_modules/eslint/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1454,7 +1502,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/file-entry-cache": { @@ -1472,7 +1520,7 @@ "node_modules/find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "dependencies": { "locate-path": "^2.0.0" @@ -1495,15 +1543,15 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -1520,10 +1568,19 @@ } } }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { @@ -1546,10 +1603,28 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, "node_modules/functions-have-names": { @@ -1562,14 +1637,14 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1578,7 +1653,7 @@ "node_modules/get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", "dev": true, "engines": { "node": ">=0.12.0" @@ -1601,15 +1676,15 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -1647,6 +1722,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -1656,7 +1758,7 @@ "node_modules/graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", "dev": true }, "node_modules/has": { @@ -1683,7 +1785,7 @@ "node_modules/has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1701,6 +1803,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -1830,7 +1944,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -1839,7 +1953,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -1859,12 +1973,12 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.1.3", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -1872,10 +1986,24 @@ "node": ">= 0.4" } }, + "node_modules/is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-bigint": { @@ -1907,9 +2035,9 @@ } }, "node_modules/is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, "engines": { "node": ">= 0.4" @@ -1919,9 +2047,9 @@ } }, "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -1948,7 +2076,7 @@ "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1978,7 +2106,7 @@ "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, "node_modules/is-negative-zero": { @@ -2075,6 +2203,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -2090,7 +2237,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isomorphic.js": { @@ -2140,9 +2287,9 @@ } }, "node_modules/jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "dependencies": { "@babel/parser": "^7.9.4", @@ -2151,7 +2298,7 @@ "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", @@ -2165,7 +2312,7 @@ "jsdoc": "jsdoc.js" }, "engines": { - "node": ">=8.15.0" + "node": ">=12.0.0" } }, "node_modules/jsdoc/node_modules/escape-string-regexp": { @@ -2192,13 +2339,13 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "dependencies": { "minimist": "^1.2.0" @@ -2214,25 +2361,25 @@ "dev": true }, "node_modules/jsx-ast-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", - "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", "dev": true, "dependencies": { - "array-includes": "^3.1.4", - "object.assign": "^4.1.2" + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" }, "engines": { "node": ">=4.0" } }, "node_modules/klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, - "engines": { - "node": ">=14.14.0" + "dependencies": { + "graceful-fs": "^4.1.9" } }, "node_modules/levn": { @@ -2249,14 +2396,14 @@ } }, "node_modules/lib0": { - "version": "0.2.51", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.51.tgz", - "integrity": "sha512-05Erb3465CxJa38LQlMz4EbetNvRna1S3BzqEjC0/pmp5cQuQSfNNmeS0722Wev1dRlMUp2Cql0gQ55krSXf2Q==", + "version": "0.2.60", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.60.tgz", + "integrity": "sha512-vzhtdUXBV8HyJnJWIZxUSH/aUVo1U4jUFRFDPVY245zFtzCl9Gld/EgvA8Jhnrio7Jn0HmGswErbPjsabYd7ow==", "dependencies": { "isomorphic.js": "^0.2.4" }, "engines": { - "node": ">=12" + "node": ">=14" }, "funding": { "type": "GitHub Sponsors ❤", @@ -2275,7 +2422,7 @@ "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "dependencies": { "graceful-fs": "^4.1.2", @@ -2290,7 +2437,7 @@ "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "dependencies": { "p-locate": "^2.0.0", @@ -2309,49 +2456,49 @@ "node_modules/lodash.assignin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", "dev": true }, "node_modules/lodash.bind": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==", "dev": true }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, "node_modules/lodash.differencewith": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", - "integrity": "sha1-uvr7yRi1UVTheRdqALsK76rIVLc=", + "integrity": "sha512-/8JFjydAS+4bQuo3CpLMBv7WxGFyk7/etOAsrQUCu0a9QVDemxv0YQ0rFyeZvqlUD314SERfNlgnlqqHmaQ0Cg==", "dev": true }, "node_modules/lodash.filter": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==", "dev": true }, "node_modules/lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true }, "node_modules/lodash.foreach": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", "dev": true }, "node_modules/lodash.map": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", "dev": true }, "node_modules/lodash.merge": { @@ -2363,31 +2510,31 @@ "node_modules/lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "node_modules/lodash.reduce": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", "dev": true }, "node_modules/lodash.reject": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==", "dev": true }, "node_modules/lodash.some": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", "dev": true }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "node_modules/loose-envify": { @@ -2440,9 +2587,9 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.2.tgz", - "integrity": "sha512-JNaekTlIwwyYGBN3zifZDxgz4bSL8sbEj58fdTZGmPSMMGXBZapFjcZk2I33Jy79c1fvCKHpF7MA/67FOTjvzA==", + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", + "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", @@ -2492,7 +2639,7 @@ "node_modules/markdownlint-cli/node_modules/commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", "dev": true, "dependencies": { "graceful-readlink": ">= 1.0.0" @@ -2580,9 +2727,9 @@ } }, "node_modules/marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -2594,7 +2741,7 @@ "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "node_modules/mime": { @@ -2622,10 +2769,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mkdirp": { "version": "1.0.4", @@ -2648,7 +2798,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/normalize-package-data": { @@ -2675,16 +2825,16 @@ "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2700,14 +2850,14 @@ } }, "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, "engines": { @@ -2718,28 +2868,28 @@ } }, "node_modules/object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" @@ -2749,27 +2899,27 @@ } }, "node_modules/object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", "dev": true, "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "engines": { "node": ">= 0.4" @@ -2781,7 +2931,7 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" @@ -2828,7 +2978,7 @@ "node_modules/p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "dependencies": { "p-limit": "^1.1.0" @@ -2840,7 +2990,7 @@ "node_modules/p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, "engines": { "node": ">=4" @@ -2861,7 +3011,7 @@ "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "dependencies": { "error-ex": "^1.3.1", @@ -2874,7 +3024,7 @@ "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "engines": { "node": ">=4" @@ -2883,7 +3033,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2931,7 +3081,7 @@ "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, "engines": { "node": ">=4" @@ -3048,7 +3198,7 @@ "node_modules/pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", "dev": true, "dependencies": { "find-up": "^2.1.0" @@ -3058,14 +3208,14 @@ } }, "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "engines": { "node": ">= 0.12.0" @@ -3113,18 +3263,18 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -3163,7 +3313,7 @@ "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -3178,7 +3328,7 @@ "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "dependencies": { "load-json-file": "^4.0.0", @@ -3192,7 +3342,7 @@ "node_modules/read-pkg-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", "dev": true, "dependencies": { "find-up": "^2.0.0", @@ -3257,25 +3407,25 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "dependencies": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3311,9 +3461,9 @@ } }, "node_modules/rollup": { - "version": "2.70.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz", - "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -3328,7 +3478,7 @@ "node_modules/rx": { "version": "2.3.24", "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", - "integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=", + "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", "dev": true }, "node_modules/safe-buffer": { @@ -3351,10 +3501,24 @@ } ] }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, "node_modules/semver": { @@ -3455,12 +3619,13 @@ "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true }, "node_modules/spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "node_modules/spdx-correct": { @@ -3490,15 +3655,15 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/standard": { @@ -3602,18 +3767,18 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", + "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4" }, "funding": { @@ -3621,26 +3786,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3661,7 +3828,7 @@ "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, "engines": { "node": ">=4" @@ -3682,7 +3849,7 @@ "node_modules/supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "dependencies": { "has-flag": "^1.0.0" @@ -3704,9 +3871,9 @@ } }, "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -3720,9 +3887,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -3744,13 +3911,13 @@ "node_modules/taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "node_modules/tree-kill": { @@ -3804,10 +3971,24 @@ "node": ">=8" } }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -3824,14 +4005,14 @@ "dev": true }, "node_modules/unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" }, "funding": { @@ -3839,9 +4020,9 @@ } }, "node_modules/underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "node_modules/union": { @@ -3868,13 +4049,13 @@ "node_modules/url-join": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "integrity": "sha512-c2H1fIgpUdwFRIru9HFno5DT73Ok8hg5oOb5AT3ayIgvCRfxgs2jyt5Slw8kEB7j3QUr6yJmMPDT/odjk7jXow==", "dev": true }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "node_modules/v8-compile-cache": { @@ -3924,6 +4105,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -3936,7 +4137,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/xdg-basedir": { @@ -3976,35 +4177,35 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", "dev": true, "requires": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.18.6" } }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, "@babel/highlight": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz", - "integrity": "sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg==", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-validator-identifier": "^7.18.6", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.17.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.9.tgz", - "integrity": "sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==", + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", + "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", "dev": true }, "@eslint/eslintrc": { @@ -4105,7 +4306,7 @@ "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, "@types/linkify-it": { @@ -4131,9 +4332,9 @@ "dev": true }, "@types/node": { - "version": "17.0.25", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.25.tgz", - "integrity": "sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "@types/resolve": { @@ -4171,9 +4372,9 @@ } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-regex": { @@ -4198,39 +4399,39 @@ "dev": true }, "array-includes": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", - "integrity": "sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "is-string": "^1.0.7" } }, "array.prototype.flat": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", - "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" } }, "array.prototype.flatmap": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz", - "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", "es-shim-unscopables": "^1.0.0" } }, @@ -4249,6 +4450,12 @@ "lodash": "^4.17.14" } }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4258,7 +4465,7 @@ "basic-auth": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", + "integrity": "sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA==", "dev": true }, "bluebird": { @@ -4270,7 +4477,7 @@ "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, "brace-expansion": { @@ -4284,9 +4491,9 @@ } }, "builtin-modules": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", - "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true }, "call-bind": { @@ -4328,7 +4535,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "supports-color": { @@ -4345,7 +4552,7 @@ "cheerio": { "version": "0.22.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", "dev": true, "requires": { "css-select": "~1.2.0", @@ -4386,7 +4593,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "colors": { @@ -4398,19 +4605,19 @@ "commander": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", + "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", "dev": true }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "concurrently": { @@ -4433,7 +4640,7 @@ "corser": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true }, "cross-spawn": { @@ -4450,7 +4657,7 @@ "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", "dev": true, "requires": { "boolbase": "~1.0.0", @@ -4493,9 +4700,9 @@ "dev": true }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", + "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", "dev": true }, "define-properties": { @@ -4553,7 +4760,7 @@ "domutils": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", "dev": true, "requires": { "dom-serializer": "0", @@ -4603,31 +4810,55 @@ } }, "es-abstract": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", - "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", "dev": true, "requires": { + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", - "get-intrinsic": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "is-callable": "^1.2.4", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.0", + "object-inspect": "^1.12.2", "object-keys": "^1.1.1", - "object.assign": "^4.1.2", - "string.prototype.trimend": "^1.0.4", - "string.prototype.trimstart": "^1.0.4", - "unbox-primitive": "^1.0.1" + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + } + }, + "es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" } }, "es-shim-unscopables": { @@ -4653,7 +4884,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true }, "eslint": { @@ -4763,9 +4994,9 @@ "dev": true }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -4797,23 +5028,23 @@ "requires": {} }, "eslint-import-resolver-node": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", - "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", "dev": true, "requires": { "debug": "^3.2.7", - "resolve": "^1.20.0" + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" } }, "eslint-module-utils": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz", - "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", "dev": true, "requires": { - "debug": "^3.2.7", - "find-up": "^2.1.0" + "debug": "^3.2.7" } }, "eslint-plugin-es": { @@ -4870,7 +5101,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true } } @@ -4935,13 +5166,14 @@ } }, "resolve": { - "version": "2.0.0-next.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz", - "integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==", + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", "dev": true, "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } } } @@ -5069,7 +5301,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "file-entry-cache": { @@ -5084,7 +5316,7 @@ "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "requires": { "locate-path": "^2.0.0" @@ -5101,21 +5333,30 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "fsevents": { @@ -5131,10 +5372,22 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", "dev": true }, "functions-have-names": { @@ -5144,20 +5397,20 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.3" } }, "get-stdin": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", "dev": true }, "get-symbol-description": { @@ -5171,15 +5424,15 @@ } }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -5202,6 +5455,24 @@ "type-fest": "^0.8.1" } }, + "globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -5211,7 +5482,7 @@ "graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", "dev": true }, "has": { @@ -5232,7 +5503,7 @@ "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", "dev": true }, "has-property-descriptors": { @@ -5244,6 +5515,12 @@ "get-intrinsic": "^1.1.1" } }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true + }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5341,13 +5618,13 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -5367,20 +5644,31 @@ "dev": true }, "internal-slot": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", - "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", "dev": true, "requires": { - "get-intrinsic": "^1.1.0", + "get-intrinsic": "^1.1.3", "has": "^1.0.3", "side-channel": "^1.0.4" } }, + "is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + } + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "is-bigint": { @@ -5403,15 +5691,15 @@ } }, "is-callable": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", - "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true }, "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "requires": { "has": "^1.0.3" @@ -5429,7 +5717,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -5450,7 +5738,7 @@ "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, "is-negative-zero": { @@ -5514,6 +5802,19 @@ "has-symbols": "^1.0.2" } }, + "is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + } + }, "is-weakref": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", @@ -5526,7 +5827,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isomorphic.js": { @@ -5571,9 +5872,9 @@ } }, "jsdoc": { - "version": "3.6.10", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.10.tgz", - "integrity": "sha512-IdQ8ppSo5LKZ9o3M+LKIIK8i00DIe5msDvG3G81Km+1dhy0XrOWD0Ji8H61ElgyEj/O9KRLokgKbAM9XX9CJAg==", + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", "dev": true, "requires": { "@babel/parser": "^7.9.4", @@ -5582,7 +5883,7 @@ "catharsis": "^0.9.0", "escape-string-regexp": "^2.0.0", "js2xmlparser": "^4.0.2", - "klaw": "^4.0.1", + "klaw": "^3.0.0", "markdown-it": "^12.3.2", "markdown-it-anchor": "^8.4.1", "marked": "^4.0.10", @@ -5616,13 +5917,13 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, "requires": { "minimist": "^1.2.0" @@ -5635,20 +5936,23 @@ "dev": true }, "jsx-ast-utils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.2.tgz", - "integrity": "sha512-HDAyJ4MNQBboGpUnHAVUNJs6X0lh058s6FuixsFGP7MgJYpD6Vasd6nzSG5iIfXu1zAYlHJ/zsOKNlrenTUBnw==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", + "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", "dev": true, "requires": { - "array-includes": "^3.1.4", - "object.assign": "^4.1.2" + "array-includes": "^3.1.5", + "object.assign": "^4.1.3" } }, "klaw": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.0.1.tgz", - "integrity": "sha512-pgsE40/SvC7st04AHiISNewaIMUbY5V/K8b21ekiPiFoYs/EYSdsGa+FJArB1d441uq4Q8zZyIxvAzkGNlBdRw==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } }, "levn": { "version": "0.4.1", @@ -5661,9 +5965,9 @@ } }, "lib0": { - "version": "0.2.51", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.51.tgz", - "integrity": "sha512-05Erb3465CxJa38LQlMz4EbetNvRna1S3BzqEjC0/pmp5cQuQSfNNmeS0722Wev1dRlMUp2Cql0gQ55krSXf2Q==", + "version": "0.2.60", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.60.tgz", + "integrity": "sha512-vzhtdUXBV8HyJnJWIZxUSH/aUVo1U4jUFRFDPVY245zFtzCl9Gld/EgvA8Jhnrio7Jn0HmGswErbPjsabYd7ow==", "requires": { "isomorphic.js": "^0.2.4" } @@ -5680,7 +5984,7 @@ "load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -5692,7 +5996,7 @@ "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "requires": { "p-locate": "^2.0.0", @@ -5708,49 +6012,49 @@ "lodash.assignin": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", "dev": true }, "lodash.bind": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==", "dev": true }, "lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, "lodash.differencewith": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", - "integrity": "sha1-uvr7yRi1UVTheRdqALsK76rIVLc=", + "integrity": "sha512-/8JFjydAS+4bQuo3CpLMBv7WxGFyk7/etOAsrQUCu0a9QVDemxv0YQ0rFyeZvqlUD314SERfNlgnlqqHmaQ0Cg==", "dev": true }, "lodash.filter": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==", "dev": true }, "lodash.flatten": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true }, "lodash.foreach": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", "dev": true }, "lodash.map": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", "dev": true }, "lodash.merge": { @@ -5762,31 +6066,31 @@ "lodash.pick": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", "dev": true }, "lodash.reduce": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", "dev": true }, "lodash.reject": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==", "dev": true }, "lodash.some": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", "dev": true }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "loose-envify": { @@ -5830,9 +6134,9 @@ } }, "markdown-it-anchor": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.2.tgz", - "integrity": "sha512-JNaekTlIwwyYGBN3zifZDxgz4bSL8sbEj58fdTZGmPSMMGXBZapFjcZk2I33Jy79c1fvCKHpF7MA/67FOTjvzA==", + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", + "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", "dev": true, "requires": {} }, @@ -5909,7 +6213,7 @@ "commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", "dev": true, "requires": { "graceful-readlink": ">= 1.0.0" @@ -5947,15 +6251,15 @@ "dev": true }, "marked": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.14.tgz", - "integrity": "sha512-HL5sSPE/LP6U9qKgngIIPTthuxC0jrfxpYMZ3LdGDD3vTnLs59m2Z7r6+LNDR3ToqEQdkKd6YaaEfJhodJmijQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, "mime": { @@ -5974,9 +6278,9 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "mkdirp": { @@ -5994,7 +6298,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "normalize-package-data": { @@ -6021,13 +6325,13 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, "object-keys": { @@ -6037,64 +6341,64 @@ "dev": true }, "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", "object-keys": "^1.1.1" } }, "object.entries": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz", - "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "object.fromentries": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz", - "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", + "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "object.hasown": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.0.tgz", - "integrity": "sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", + "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "object.values": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", - "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" @@ -6132,7 +6436,7 @@ "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "requires": { "p-limit": "^1.1.0" @@ -6141,7 +6445,7 @@ "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true }, "parent-module": { @@ -6156,7 +6460,7 @@ "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "requires": { "error-ex": "^1.3.1", @@ -6166,13 +6470,13 @@ "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-key": { @@ -6205,7 +6509,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true }, "pkg-conf": { @@ -6291,21 +6595,21 @@ "pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", "dev": true, "requires": { "find-up": "^2.1.0" } }, "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "version": "1.0.32", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", + "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "async": "^2.6.4", + "debug": "^3.2.7", + "mkdirp": "^0.5.6" }, "dependencies": { "mkdirp": { @@ -6343,15 +6647,15 @@ } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, "qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, "requires": { "side-channel": "^1.0.4" @@ -6378,7 +6682,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true } } @@ -6392,7 +6696,7 @@ "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "requires": { "load-json-file": "^4.0.0", @@ -6403,7 +6707,7 @@ "read-pkg-up": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", "dev": true, "requires": { "find-up": "^2.0.0", @@ -6447,25 +6751,25 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -6486,9 +6790,9 @@ } }, "rollup": { - "version": "2.70.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.70.2.tgz", - "integrity": "sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -6497,7 +6801,7 @@ "rx": { "version": "2.3.24", "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", - "integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=", + "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", "dev": true }, "safe-buffer": { @@ -6506,10 +6810,21 @@ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, "secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", "dev": true }, "semver": { @@ -6590,7 +6905,7 @@ "spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=", + "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", "dev": true }, "spdx-correct": { @@ -6620,15 +6935,15 @@ } }, "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "standard": { @@ -6688,39 +7003,41 @@ } }, "string.prototype.matchall": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz", - "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", + "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.1", - "get-intrinsic": "^1.1.1", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.1", + "regexp.prototype.flags": "^1.4.3", "side-channel": "^1.0.4" } }, "string.prototype.trimend": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", - "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "string.prototype.trimstart": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", - "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", "dev": true, "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" } }, "strip-ansi": { @@ -6735,7 +7052,7 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-json-comments": { @@ -6747,7 +7064,7 @@ "supports-color": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", "dev": true, "requires": { "has-flag": "^1.0.0" @@ -6760,9 +7077,9 @@ "dev": true }, "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -6773,9 +7090,9 @@ }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -6795,13 +7112,13 @@ "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", "dev": true }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "tree-kill": { @@ -6846,10 +7163,21 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + } + }, "typescript": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", - "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true }, "uc.micro": { @@ -6859,21 +7187,21 @@ "dev": true }, "unbox-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", - "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, "requires": { - "function-bind": "^1.1.1", - "has-bigints": "^1.0.1", - "has-symbols": "^1.0.2", + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", "which-boxed-primitive": "^1.0.2" } }, "underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "union": { @@ -6897,13 +7225,13 @@ "url-join": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "integrity": "sha512-c2H1fIgpUdwFRIru9HFno5DT73Ok8hg5oOb5AT3ayIgvCRfxgs2jyt5Slw8kEB7j3QUr6yJmMPDT/odjk7jXow==", "dev": true }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, "v8-compile-cache": { @@ -6944,6 +7272,20 @@ "is-symbol": "^1.0.3" } }, + "which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -6953,7 +7295,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "xdg-basedir": { diff --git a/package.json b/package.json index 5989d8795..36ad9a195 100644 --- a/package.json +++ b/package.json @@ -81,13 +81,13 @@ "@rollup/plugin-commonjs": "^17.0.0", "@rollup/plugin-node-resolve": "^11.2.1", "concurrently": "^3.6.1", + "typescript": "^4.9.5", "http-server": "^0.12.3", "jsdoc": "^3.6.7", "markdownlint-cli": "^0.23.2", "rollup": "^2.60.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^4.4.4", "y-protocols": "^1.0.5" } } diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 617ea19b2..144cfc3cd 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -324,9 +324,9 @@ export class AbstractType { } /** - * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder + * @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder */ - _write (encoder) { } + _write (_encoder) { } /** * The first non-deleted item @@ -344,9 +344,9 @@ export class AbstractType { * Must be implemented by each type. * * @param {Transaction} transaction - * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. + * @param {Set} _parentSubs Keys changed on this type. `null` if list was modified. */ - _callObserver (transaction, parentSubs) { + _callObserver (transaction, _parentSubs) { if (!transaction.local && this._searchMarker) { this._searchMarker.length = 0 } diff --git a/src/types/YArray.js b/src/types/YArray.js index 58c7287b2..45b2fb565 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -58,11 +58,14 @@ export class YArray extends AbstractType { /** * Construct a new YArray containing the specified items. - * @template T + * @template {Object|Array|number|null|string|Uint8Array} T * @param {Array} items * @return {YArray} */ static from (items) { + /** + * @type {YArray} + */ const a = new YArray() a.push(items) return a @@ -84,6 +87,9 @@ export class YArray extends AbstractType { this._prelimContent = null } + /** + * @return {YArray} + */ _copy () { return new YArray() } @@ -92,9 +98,12 @@ export class YArray extends AbstractType { * @return {YArray} */ clone () { + /** + * @type {YArray} + */ const arr = new YArray() arr.insert(0, this.toArray().map(el => - el instanceof AbstractType ? el.clone() : el + el instanceof AbstractType ? /** @type {typeof el} */ (el.clone()) : el )) return arr } @@ -133,7 +142,7 @@ export class YArray extends AbstractType { insert (index, content) { if (this.doc !== null) { transact(this.doc, transaction => { - typeListInsertGenerics(transaction, this, index, content) + typeListInsertGenerics(transaction, this, index, /** @type {any} */ (content)) }) } else { /** @type {Array} */ (this._prelimContent).splice(index, 0, ...content) @@ -150,7 +159,7 @@ export class YArray extends AbstractType { push (content) { if (this.doc !== null) { transact(this.doc, transaction => { - typeListPushGenerics(transaction, this, content) + typeListPushGenerics(transaction, this, /** @type {any} */ (content)) }) } else { /** @type {Array} */ (this._prelimContent).push(...content) @@ -259,9 +268,9 @@ export class YArray extends AbstractType { } /** - * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder + * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder * * @private * @function */ -export const readYArray = decoder => new YArray() +export const readYArray = _decoder => new YArray() diff --git a/src/types/YMap.js b/src/types/YMap.js index a31b9e039..c640ae543 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -81,6 +81,9 @@ export class YMap extends AbstractType { this._prelimContent = null } + /** + * @return {YMap} + */ _copy () { return new YMap() } @@ -89,9 +92,12 @@ export class YMap extends AbstractType { * @return {YMap} */ clone () { + /** + * @type {YMap} + */ const map = new YMap() this.forEach((value, key) => { - map.set(key, value instanceof AbstractType ? value.clone() : value) + map.set(key, value instanceof AbstractType ? /** @type {typeof value} */ (value.clone()) : value) }) return map } @@ -207,7 +213,7 @@ export class YMap extends AbstractType { set (key, value) { if (this.doc !== null) { transact(this.doc, transaction => { - typeMapSet(transaction, this, key, value) + typeMapSet(transaction, this, key, /** @type {any} */ (value)) }) } else { /** @type {Map} */ (this._prelimContent).set(key, value) @@ -241,7 +247,7 @@ export class YMap extends AbstractType { clear () { if (this.doc !== null) { transact(this.doc, transaction => { - this.forEach(function (value, key, map) { + this.forEach(function (_value, key, map) { typeMapDelete(transaction, map, key) }) }) @@ -259,9 +265,9 @@ export class YMap extends AbstractType { } /** - * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder + * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder * * @private * @function */ -export const readYMap = decoder => new YMap() +export const readYMap = _decoder => new YMap() diff --git a/src/types/YText.js b/src/types/YText.js index 887eb6cb3..788f67b40 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -251,7 +251,7 @@ const insertAttributes = (transaction, parent, currPos, attributes) => { * @function **/ const insertText = (transaction, parent, currPos, text, attributes) => { - currPos.currentAttributes.forEach((val, key) => { + currPos.currentAttributes.forEach((_val, key) => { if (attributes[key] === undefined) { attributes[key] = null } @@ -927,7 +927,7 @@ export class YText extends AbstractType { * Apply a {@link Delta} on this shared YText type. * * @param {any} delta The changes to apply on this element. - * @param {object} [opts] + * @param {object} opts * @param {boolean} [opts.sanitize] Sanitize input delta. Removes ending newlines if set to true. * * @@ -1229,12 +1229,11 @@ export class YText extends AbstractType { * * @note Xml-Text nodes don't have attributes. You can use this feature to assign properties to complete text-blocks. * - * @param {Snapshot} [snapshot] * @return {Object} A JSON Object that describes the attributes. * * @public */ - getAttributes (snapshot) { + getAttributes () { return typeMapGetAll(this) } @@ -1247,10 +1246,10 @@ export class YText extends AbstractType { } /** - * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder + * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder * @return {YText} * * @private * @function */ -export const readYText = decoder => new YText() +export const readYText = _decoder => new YText() diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 59c51cffb..ce8335ce3 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -9,7 +9,7 @@ import { typeMapGetAll, typeListForEach, YXmlElementRefID, - YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Snapshot, Doc, Item // eslint-disable-line + YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line } from '../internals.js' /** diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 8c4622362..66c44f9ab 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -17,7 +17,7 @@ import { transact, typeListGet, typeListSlice, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -407,7 +407,7 @@ export class YXmlFragment extends AbstractType { /** * Executes a provided function on once on overy child element. * - * @param {function(YXmlElement|YXmlText,number, typeof this):void} f A function to execute on every element of this YArray. + * @param {function(YXmlElement|YXmlText,number, typeof self):void} f A function to execute on every element of this YArray. */ forEach (f) { typeListForEach(this, f) @@ -427,10 +427,10 @@ export class YXmlFragment extends AbstractType { } /** - * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder + * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder * @return {YXmlFragment} * * @private * @function */ -export const readYXmlFragment = decoder => new YXmlFragment() +export const readYXmlFragment = _decoder => new YXmlFragment() diff --git a/src/utils/Doc.js b/src/utils/Doc.js index ace76392b..e248e9939 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -38,7 +38,7 @@ export const generateNewClientId = random.uint32 */ export class Doc extends Observable { /** - * @param {DocOpts} [opts] configuration + * @param {DocOpts} opts configuration */ constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true } = {}) { super() diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js index 6ebd33b18..d9e44f129 100644 --- a/src/utils/PermanentUserData.js +++ b/src/utils/PermanentUserData.js @@ -71,7 +71,7 @@ export class PermanentUserData { * @param {Doc} doc * @param {number} clientid * @param {string} userDescription - * @param {Object} [conf] + * @param {Object} conf * @param {function(Transaction, DeleteSet):boolean} [conf.filter] */ setUserMapping (doc, clientid, userDescription, { filter = () => true } = {}) { @@ -84,7 +84,7 @@ export class PermanentUserData { users.set(userDescription, user) } user.get('ids').push([clientid]) - users.observe(event => { + users.observe(_event => { setTimeout(() => { const userOverwrite = users.get(userDescription) if (userOverwrite !== user) { diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 724958728..dce1b204e 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -30,9 +30,9 @@ export const testOriginInTransaction = _tc => { /** * Client id should be changed when an instance receives updates from another client using the same client id. * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testClientIdDuplicateChange = tc => { +export const testClientIdDuplicateChange = _tc => { const doc1 = new Y.Doc() doc1.clientID = 0 const doc2 = new Y.Doc() @@ -44,9 +44,9 @@ export const testClientIdDuplicateChange = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testGetTypeEmptyId = tc => { +export const testGetTypeEmptyId = _tc => { const doc1 = new Y.Doc() doc1.getText('').insert(0, 'h') doc1.getText().insert(1, 'i') @@ -57,9 +57,9 @@ export const testGetTypeEmptyId = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testToJSON = tc => { +export const testToJSON = _tc => { const doc = new Y.Doc() t.compare(doc.toJSON(), {}, 'doc.toJSON yields empty object') @@ -84,9 +84,9 @@ export const testToJSON = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSubdoc = tc => { +export const testSubdoc = _tc => { const doc = new Y.Doc() doc.load() // doesn't do anything { @@ -151,9 +151,9 @@ export const testSubdoc = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSubdocLoadEdgeCases = tc => { +export const testSubdocLoadEdgeCases = _tc => { const ydoc = new Y.Doc() const yarray = ydoc.getArray() const subdoc1 = new Y.Doc() @@ -198,9 +198,9 @@ export const testSubdocLoadEdgeCases = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSubdocLoadEdgeCasesAutoload = tc => { +export const testSubdocLoadEdgeCasesAutoload = _tc => { const ydoc = new Y.Doc() const yarray = ydoc.getArray() const subdoc1 = new Y.Doc({ autoLoad: true }) @@ -240,9 +240,9 @@ export const testSubdocLoadEdgeCasesAutoload = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSubdocsUndo = tc => { +export const testSubdocsUndo = _tc => { const ydoc = new Y.Doc() const elems = ydoc.getXmlFragment() const undoManager = new Y.UndoManager(elems) @@ -255,9 +255,9 @@ export const testSubdocsUndo = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testLoadDocs = async tc => { +export const testLoadDocs = async _tc => { const ydoc = new Y.Doc() t.assert(ydoc.isLoaded === false) let loadedEvent = false diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 83f31a48f..e12d55c9c 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -455,9 +455,9 @@ export const testChangeEvent = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testYmapEventExceptionsShouldCompleteTransaction = tc => { +export const testYmapEventExceptionsShouldCompleteTransaction = _tc => { const doc = new Y.Doc() const map = doc.getMap('map') From 7445a9ce5f4efd323c54391288efd06cac0e83f7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 31 Jan 2023 12:56:07 +0100 Subject: [PATCH 022/362] add whenSynced and isSynced property with refined logic --- src/utils/Doc.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ tests/doc.tests.js | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index e248e9939..4eabd7c60 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -72,13 +72,57 @@ export class Doc extends Observable { this.shouldLoad = shouldLoad this.autoLoad = autoLoad this.meta = meta + /** + * This is set to true when the persistence provider loaded the document from the database or when the `sync` event fires. + * Note that not all providers implement this feature. Provider authors are encouraged to fire the `load` event when the doc content is loaded from the database. + * + * @type {boolean} + */ this.isLoaded = false + /** + * This is set to true when the connection provider has successfully synced with a backend. + * Note that when using peer-to-peer providers this event may not provide very useful. + * Also note that not all providers implement this feature. Provider authors are encouraged to fire + * the `sync` event when the doc has been synced (with `true` as a parameter) or if connection is + * lost (with false as a parameter). + */ + this.isSynced = false + /** + * Promise that resolves once the document has been loaded from a presistence provider. + */ this.whenLoaded = promise.create(resolve => { this.on('load', () => { this.isLoaded = true resolve(this) }) }) + const provideSyncedPromise = () => promise.create(resolve => { + /** + * @param {boolean} isSynced + */ + const eventHandler = (isSynced) => { + if (isSynced === undefined || isSynced === true) { + this.off('sync', eventHandler) + resolve() + } + } + this.on('sync', eventHandler) + }) + this.on('sync', isSynced => { + if (isSynced === false && this.isSynced) { + this.whenSynced = provideSyncedPromise() + } + this.isSynced = isSynced === undefined || isSynced === true + if (!this.isLoaded) { + this.emit('load', []) + } + }) + /** + * Promise that resolves once the document has been synced with a backend. + * This promise is recreated when the connection is lost. + * Note the documentation about the `isSynced` property. + */ + this.whenSynced = provideSyncedPromise() } /** diff --git a/tests/doc.tests.js b/tests/doc.tests.js index dce1b204e..c62119776 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -257,7 +257,7 @@ export const testSubdocsUndo = _tc => { /** * @param {t.TestCase} _tc */ -export const testLoadDocs = async _tc => { +export const testLoadDocsEvent = async _tc => { const ydoc = new Y.Doc() t.assert(ydoc.isLoaded === false) let loadedEvent = false @@ -269,3 +269,44 @@ export const testLoadDocs = async _tc => { t.assert(loadedEvent) t.assert(ydoc.isLoaded) } + +/** + * @param {t.TestCase} _tc + */ +export const testSyncDocsEvent = async _tc => { + const ydoc = new Y.Doc() + t.assert(ydoc.isLoaded === false) + t.assert(ydoc.isSynced === false) + let loadedEvent = false + ydoc.once('load', () => { + loadedEvent = true + }) + let syncedEvent = false + ydoc.once('sync', /** @param {any} isSynced */ (isSynced) => { + syncedEvent = true + t.assert(isSynced) + }) + ydoc.emit('sync', [true, ydoc]) + await ydoc.whenLoaded + const oldWhenSynced = ydoc.whenSynced + await ydoc.whenSynced + t.assert(loadedEvent) + t.assert(syncedEvent) + t.assert(ydoc.isLoaded) + t.assert(ydoc.isSynced) + let loadedEvent2 = false + ydoc.on('load', () => { + loadedEvent2 = true + }) + let syncedEvent2 = false + ydoc.on('sync', (isSynced) => { + syncedEvent2 = true + t.assert(isSynced === false) + }) + ydoc.emit('sync', [false, ydoc]) + t.assert(!loadedEvent2) + t.assert(syncedEvent2) + t.assert(ydoc.isLoaded) + t.assert(!ydoc.isSynced) + t.assert(ydoc.whenSynced !== oldWhenSynced) +} From e0a2f11db325ae570ca273c4e75a1b9e8655491a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 31 Jan 2023 12:57:56 +0100 Subject: [PATCH 023/362] 13.5.45 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3f98add1..b3cb137b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.44", + "version": "13.5.45", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.44", + "version": "13.5.45", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index 36ad9a195..f7497e7e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.44", + "version": "13.5.45", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From e9189365ee0a3337435d876b687b49d7cc0f4669 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 13 Feb 2023 14:27:57 +0100 Subject: [PATCH 024/362] add debugging case for #474 - unfininished --- tests/y-text.tests.js | 409 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 4f2fe090f..fb64d95d8 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -5,6 +5,415 @@ import * as math from 'lib0/math' const { init, compare } = Y +/** + * https://github.com/yjs/yjs/issues/474 + * @todo Remove debug: 127.0.0.1:8080/test.html?filter=\[88/ + * @param {t.TestCase} _tc + */ +export const testDeltaBug = _tc => { + const initialDelta = [{ + attributes: { + 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' + }, + insert: '\n' + }, + { + attributes: { + 'table-col': { + width: '150' + } + }, + insert: '\n\n\n' + }, + { + attributes: { + 'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-apba4k' + }, + row: 'row-6kv2ls', + cell: 'cell-apba4k', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-a8qf0r' + }, + row: 'row-6kv2ls', + cell: 'cell-a8qf0r', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-oi9ikb' + }, + row: 'row-6kv2ls', + cell: 'cell-oi9ikb', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-dt6ks2' + }, + row: 'row-d1sv2g', + cell: 'cell-dt6ks2', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-qah2ay' + }, + row: 'row-d1sv2g', + cell: 'cell-qah2ay', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-468a69b5-9332-450b-9107-381d593de249', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-fpcz5a' + }, + row: 'row-d1sv2g', + cell: 'cell-fpcz5a', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-zrhylp' + }, + row: 'row-pflz90', + cell: 'cell-zrhylp', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-s1q9nt' + }, + row: 'row-pflz90', + cell: 'cell-s1q9nt', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-20b0j9' + }, + row: 'row-pflz90', + cell: 'cell-20b0j9', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3' + }, + insert: '\n' + }, + { + insert: 'Content after table' + }, + { + attributes: { + 'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce' + }, + insert: '\n' + } + ] + const ydoc1 = new Y.Doc() + const ytext = ydoc1.getText() + ytext.applyDelta(initialDelta) + const addingDash = [ + { + retain: 12 + }, + { + insert: '-' + } + ] + ytext.applyDelta(addingDash) + const addingSpace = [ + { + retain: 13 + }, + { + insert: ' ' + } + ] + ytext.applyDelta(addingSpace) + debugger + const addingList = [ + { + retain: 12 + }, + { + delete: 2 + }, + { + retain: 1, + attributes: { + // Clear table line attribute + 'table-cell-line': null, + // Add list attribute in place of table-cell-line + list: { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-20b0j9', + list: 'bullet' + } + } + } + ] + ytext.applyDelta(addingList) + const result = ytext.toDelta() + const expectedResult = [ + { + attributes: { + 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' + }, + insert: '\n' + }, + { + attributes: { + 'table-col': { + width: '150' + } + }, + insert: '\n\n\n' + }, + { + attributes: { + 'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-apba4k' + }, + row: 'row-6kv2ls', + cell: 'cell-apba4k', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-a8qf0r' + }, + row: 'row-6kv2ls', + cell: 'cell-a8qf0r', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-oi9ikb' + }, + row: 'row-6kv2ls', + cell: 'cell-oi9ikb', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-dt6ks2' + }, + row: 'row-d1sv2g', + cell: 'cell-dt6ks2', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-qah2ay' + }, + row: 'row-d1sv2g', + cell: 'cell-qah2ay', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-468a69b5-9332-450b-9107-381d593de249', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-fpcz5a' + }, + row: 'row-d1sv2g', + cell: 'cell-fpcz5a', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-zrhylp' + }, + row: 'row-pflz90', + cell: 'cell-zrhylp', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + attributes: { + 'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-s1q9nt' + }, + row: 'row-pflz90', + cell: 'cell-s1q9nt', + rowspan: '1', + colspan: '1' + }, + insert: '\n' + }, + { + insert: '\n', + // This attibutes has only list and no table-cell-line + attributes: { + list: { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-20b0j9', + list: 'bullet' + }, + 'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484', + row: 'row-pflz90', + cell: 'cell-20b0j9', + rowspan: '1', + colspan: '1' + } + }, + // No table-cell-line below here + { + attributes: { + 'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3' + }, + insert: '\n' + }, + { + insert: 'Content after table' + }, + { + attributes: { + 'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce' + }, + insert: '\n' + } + ] + t.compare(result, expectedResult) + debugger +} + /** * In this test we are mainly interested in the cleanup behavior and whether the resulting delta makes sense. * It is fine if the resulting delta is not minimal. But applying the delta to a rich-text editor should result in a From 1c999b250e8ffe7409bb1795a9ac13df9cb5be15 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 14 Feb 2023 16:16:14 +0100 Subject: [PATCH 025/362] fix #474 - formatting bug --- src/types/YText.js | 9 +++++++-- tests/y-text.tests.js | 2 -- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 788f67b40..d1cdf781d 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -382,12 +382,17 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt switch (content.constructor) { case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (content) - if ((endAttributes.get(key) || null) !== value || (startAttributes.get(key) || null) === value) { + const startAttrValue = startAttributes.get(key) || null + if ((endAttributes.get(key) || null) !== value || startAttrValue === value) { // Either this format is overwritten or it is not necessary because the attribute already existed. start.delete(transaction) cleanups++ if (!reachedEndOfCurr && (currAttributes.get(key) || null) === value && (startAttributes.get(key) || null) !== value) { - currAttributes.delete(key) + if (startAttrValue === null) { + currAttributes.delete(key) + } else { + currAttributes.set(key, startAttrValue) + } } } break diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index fb64d95d8..486226737 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -206,7 +206,6 @@ export const testDeltaBug = _tc => { } ] ytext.applyDelta(addingSpace) - debugger const addingList = [ { retain: 12 @@ -411,7 +410,6 @@ export const testDeltaBug = _tc => { } ] t.compare(result, expectedResult) - debugger } /** From ea7ad07f34cc64cbd5d30e3fe00fa2c5476dec9f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 14 Feb 2023 16:21:01 +0100 Subject: [PATCH 026/362] 13.5.46 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3cb137b6..b108c3fa9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.45", + "version": "13.5.46", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.45", + "version": "13.5.46", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index f7497e7e8..c620385bd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.45", + "version": "13.5.46", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2576d4efcada261aa973ccbc57f5dd71bd4fbfcf Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Feb 2023 14:35:28 +0100 Subject: [PATCH 027/362] increasing sort of ds encoding --- src/types/YXmlFragment.js | 3 ++- src/utils/DeleteSet.js | 4 ++-- src/utils/Doc.js | 2 +- src/utils/encoding.js | 7 ++++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 66c44f9ab..b229a4acc 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -21,6 +21,7 @@ import { } from '../internals.js' import * as error from 'lib0/error' +import * as array from 'lib0/array' /** * Define the elements to which a set of CSS queries apply. @@ -237,7 +238,7 @@ export class YXmlFragment extends AbstractType { querySelectorAll (query) { query = query.toUpperCase() // @ts-ignore - return Array.from(new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query)) + return array.from(new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query)) } /** diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 32fd089b4..948422742 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -221,8 +221,8 @@ export const writeDeleteSet = (encoder, ds) => { encoding.writeVarUint(encoder.restEncoder, ds.clients.size) // Ensure that the delete set is written in a deterministic order - Array.from(ds.clients.entries()) - .sort((clientA, clientB) => clientA[0] - clientB[0]) + array.from(ds.clients.entries()) + .sort((a, b) => b[0] - a[0]) .forEach(([client, dsitems]) => { encoder.resetDsCurVal() encoding.writeVarUint(encoder.restEncoder, client) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 4eabd7c60..9588d3621 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -147,7 +147,7 @@ export class Doc extends Observable { } getSubdocGuids () { - return new Set(Array.from(this.subdocs).map(doc => doc.guid)) + return new Set(array.from(this.subdocs).map(doc => doc.guid)) } /** diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 29af8bc63..56e5dac17 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -45,6 +45,7 @@ import * as decoding from 'lib0/decoding' import * as binary from 'lib0/binary' import * as map from 'lib0/map' import * as math from 'lib0/math' +import * as array from 'lib0/array' /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder @@ -96,7 +97,7 @@ export const writeClientsStructs = (encoder, store, _sm) => { encoding.writeVarUint(encoder.restEncoder, sm.size) // Write items with higher client ids first // This heavily improves the conflict algorithm. - Array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => { + array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => { // @ts-ignore writeStructs(encoder, store.clients.get(client), client, clock) }) @@ -231,7 +232,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { */ const stack = [] // sort them so that we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user. - let clientsStructRefsIds = Array.from(clientsStructRefs.keys()).sort((a, b) => a - b) + let clientsStructRefsIds = array.from(clientsStructRefs.keys()).sort((a, b) => a - b) if (clientsStructRefsIds.length === 0) { return null } @@ -601,7 +602,7 @@ export const decodeStateVector = decodedState => readStateVector(new DSDecoderV1 */ export const writeStateVector = (encoder, sv) => { encoding.writeVarUint(encoder.restEncoder, sv.size) - Array.from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => { + array.from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => { encoding.writeVarUint(encoder.restEncoder, client) // @todo use a special client decoder that is based on mapping encoding.writeVarUint(encoder.restEncoder, clock) }) From 658c520b93c43adec1c81a4dbd7ce61f94542471 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Feb 2023 14:37:24 +0100 Subject: [PATCH 028/362] 13.5.47 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b108c3fa9..50c0584c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.46", + "version": "13.5.47", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.46", + "version": "13.5.47", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index c620385bd..77272de79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.46", + "version": "13.5.47", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From bf338d8040011b63caab08d7a901058a13335b25 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 2 Mar 2023 19:08:01 +0100 Subject: [PATCH 029/362] fix attribute update issue - fixes #503 --- src/types/YText.js | 10 +- tests/y-text.tests.js | 1235 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1242 insertions(+), 3 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index d1cdf781d..83b02aa63 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -367,15 +367,16 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt const endAttributes = map.copy(currAttributes) while (end && (!end.countable || end.deleted)) { if (!end.deleted && end.content.constructor === ContentFormat) { + // @todo should set endAttributes[end.key] = end and then compare identities instead of values updateCurrentAttributes(endAttributes, /** @type {ContentFormat} */ (end.content)) } end = end.right } let cleanups = 0 - let reachedEndOfCurr = false + let reachedCurr = false while (start !== end) { if (curr === start) { - reachedEndOfCurr = true + reachedCurr = true } if (!start.deleted) { const content = start.content @@ -387,7 +388,7 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt // Either this format is overwritten or it is not necessary because the attribute already existed. start.delete(transaction) cleanups++ - if (!reachedEndOfCurr && (currAttributes.get(key) || null) === value && (startAttributes.get(key) || null) !== value) { + if (!reachedCurr && (currAttributes.get(key) || null) === value && startAttrValue !== value) { if (startAttrValue === null) { currAttributes.delete(key) } else { @@ -395,6 +396,9 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt } } } + if (!reachedCurr && !start.deleted) { + updateCurrentAttributes(currAttributes, /** @type {ContentFormat} */ (content)) + } break } } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 486226737..cd44142fb 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -412,6 +412,1241 @@ export const testDeltaBug = _tc => { t.compare(result, expectedResult) } +/** + * https://github.com/yjs/yjs/issues/503 + * @param {t.TestCase} _tc + */ +export const testDeltaBug2 = _tc => { + const initialContent = [ + { insert: "Thomas' section" }, + { + insert: '\n', + attributes: { 'block-id': 'block-61ae80ac-a469-4eae-bac9-3b6a2c380118' } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-d265d93f-1cc7-40ee-bb58-8270fca2619f' } + }, + { insert: '123' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-592a7bee-76a3-4e28-9c25-7a84344f8813', + list: { list: 'toggled', 'toggle-id': 'list-66xfft' } + } + }, + { insert: '456' }, + { + insert: '\n', + attributes: { + indent: 1, + 'block-id': 'block-3ee2bd70-b97f-45b2-9115-f1e8910235b1', + list: { list: 'toggled', 'toggle-id': 'list-6vh0t0' } + } + }, + { insert: '789' }, + { + insert: '\n', + attributes: { + indent: 1, + 'block-id': 'block-78150cf3-9bb5-4dea-a6f5-0ce1d2a98b9c', + list: { list: 'toggled', 'toggle-id': 'list-7jr0l2' } + } + }, + { insert: '901' }, + { + insert: '\n', + attributes: { + indent: 1, + 'block-id': 'block-13c6416f-f522-41d5-9fd4-ce4eb1cde5ba', + list: { list: 'toggled', 'toggle-id': 'list-7uk8qu' } + } + }, + { + insert: { + slash_command: { + id: 'doc_94zq-2436', + sessionId: 'nkwc70p2j', + replace: '/' + } + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-8a1d2bb6-23c2-4bcf-af3c-3919ffea1697' } + }, + { insert: '\n\n', attributes: { 'table-col': { width: '150' } } }, + { + insert: '\n', + attributes: { 'table-col': { width: '150' } } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-84ec3ea4-da6a-4e03-b430-0e5f432936a9', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-blmd4s', + cell: 'cell-m0u5za' + }, + row: 'row-blmd4s', + cell: 'cell-m0u5za', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-83144ca8-aace-401e-8aa5-c05928a8ccf0', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-blmd4s', + cell: 'cell-1v8s8t' + }, + row: 'row-blmd4s', + cell: 'cell-1v8s8t', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-9a493387-d27f-4b58-b2f7-731dfafda32a', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-blmd4s', + cell: 'cell-126947' + }, + row: 'row-blmd4s', + cell: 'cell-126947', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-3484f86e-ae42-440f-8de6-857f0d8011ea', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-hmmljo', + cell: 'cell-wvutl9' + }, + row: 'row-hmmljo', + cell: 'cell-wvutl9', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d4e0b741-9dea-47a5-85e1-4ded0efbc89d', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-hmmljo', + cell: 'cell-nkablr' + }, + row: 'row-hmmljo', + cell: 'cell-nkablr', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-352f0d5a-d1b9-422f-b136-4bacefd00b1a', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-hmmljo', + cell: 'cell-n8xtd0' + }, + row: 'row-hmmljo', + cell: 'cell-n8xtd0', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-95823e57-f29c-44cf-a69d-2b4494b7144b', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ev4xwq', + cell: 'cell-ua9bvu' + }, + row: 'row-ev4xwq', + cell: 'cell-ua9bvu', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-cde5c027-15d3-4780-9e76-1e1a9d97a8e8', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ev4xwq', + cell: 'cell-7bwuvk' + }, + row: 'row-ev4xwq', + cell: 'cell-7bwuvk', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-11a23ed4-b04d-4e45-8065-8120889cd4a4', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ev4xwq', + cell: 'cell-aouka5' + }, + row: 'row-ev4xwq', + cell: 'cell-aouka5', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-15b4483c-da98-4ded-91d3-c3d6ebc82582' } + }, + { insert: { divider: true } }, + { + insert: '\n', + attributes: { 'block-id': 'block-68552c8e-b57b-4f4a-9f36-6cc1ef6b3461' } + }, + { insert: 'jklasjdf' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-c8b2df7d-8ec5-4dd4-81f1-8d8efc40b1b4', + list: { list: 'toggled', 'toggle-id': 'list-9ss39s' } + } + }, + { insert: 'asdf' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-4f252ceb-14da-49ae-8cbd-69a701d18e2a', + list: { list: 'toggled', 'toggle-id': 'list-uvo013' } + } + }, + { insert: 'adg' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533', + list: { list: 'toggled', 'toggle-id': 'list-k53iwe' } + } + }, + { insert: 'asdfasdfasdf' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533', + list: { list: 'none' }, + indent: 1 + } + }, + { insert: 'asdf' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-f406f76d-f338-4261-abe7-5c9131f7f1ad', + list: { list: 'toggled', 'toggle-id': 'list-en86ur' } + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-be18141c-9b6b-434e-8fd0-2c214437d560' } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-36922db3-4af5-48a1-9ea4-0788b3b5d7cf' } + }, + { insert: { table_content: true } }, + { insert: ' ' }, + { + insert: { + slash_command: { + id: 'doc_94zq-2436', + sessionId: 'hiyrt6fny', + replace: '/' + } + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-4b545085-114d-4d07-844c-789710ec3aab', + layout: + '12d887e1-d1a2-4814-a1a3-0c904e950b46_1185cd29-ef1b-45d5-8fda-51a70b704e64', + 'layout-width': '0.25' + } + }, + { + insert: '\n', + attributes: { + + 'block-id': 'block-4d3f2321-33d1-470e-9b7c-d5a683570148', + layout: + '12d887e1-d1a2-4814-a1a3-0c904e950b46_75523ea3-c67f-4f5f-a85f-ac7c8fc0a992', + 'layout-width': '0.5' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-4c7ae1e6-758e-470f-8d7c-ae0325e4ee8a', + layout: + '12d887e1-d1a2-4814-a1a3-0c904e950b46_54c740ef-fd7b-48c6-85aa-c14e1bfc9297', + 'layout-width': '0.25' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-2d6ff0f4-ff00-42b7-a8e2-b816463d8fb5' } + }, + { insert: { divider: true } }, + { + insert: '\n', + attributes: { 'table-col': { width: '150' } } + }, + { insert: '\n', attributes: { 'table-col': { width: '154' } } }, + { + insert: '\n', + attributes: { 'table-col': { width: '150' } } + }, + + { + insert: '\n', + attributes: { + 'block-id': 'block-38545d56-224b-464c-b779-51fcec24dbbf', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-q0qfck', + cell: 'cell-hmapv4' + }, + row: 'row-q0qfck', + cell: 'cell-hmapv4', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d413a094-5f52-4fd4-a4aa-00774f6fdb44', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-q0qfck', + cell: 'cell-c0czb2' + }, + row: 'row-q0qfck', + cell: 'cell-c0czb2', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-ff855cbc-8871-4e0a-9ba7-de0c1c2aa585', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-q0qfck', + cell: 'cell-hcpqmm' + }, + row: 'row-q0qfck', + cell: 'cell-hcpqmm', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-4841e6ee-fef8-4473-bf04-f5ba62db17f0', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-etopyl', + cell: 'cell-0io73v' + }, + row: 'row-etopyl', + cell: 'cell-0io73v', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-adeec631-d4fe-4f38-9d5e-e67ba068bd24', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-etopyl', + cell: 'cell-gt2waa' + }, + row: 'row-etopyl', + cell: 'cell-gt2waa', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d38a7308-c858-4ce0-b1f3-0f9092384961', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-etopyl', + cell: 'cell-os9ksy' + }, + row: 'row-etopyl', + cell: 'cell-os9ksy', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-a9df6568-1838-40d1-9d16-3c073b6ce169', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-hbx9ri' + }, + row: 'row-0jwjg3', + cell: 'cell-hbx9ri', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-e26a0cf2-fe62-44a5-a4ca-8678a56d62f1', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-yg5m2w' + }, + row: 'row-0jwjg3', + cell: 'cell-yg5m2w', + rowspan: '1', + colspan: '1' + } + }, + { insert: 'a' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-bfbc5ac2-7417-44b9-9aa5-8e36e4095627', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { insert: 'b' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-f011c089-6389-47c0-8396-7477a29aa56f', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { insert: 'c' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-4497788d-1e02-4fd5-a80a-48b61a6185cb', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { insert: 'd' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-5d73a2c7-f98b-47c7-a3f5-0d8527962b02', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { insert: 'e' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-bfda76ee-ffdd-45db-a22e-a6707e11cf68', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { insert: 'd' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-35242e64-a69d-4cdb-bd85-2a93766bfab4', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { insert: 'f' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-8baa22c8-491b-4f1b-9502-44179d5ae744', + list: { + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-7fa64af0-6974-4205-8cee-529f8bd46852' } + }, + { insert: { divider: true } }, + { insert: "Brandon's Section" }, + { + insert: '\n', + attributes: { + header: 2, + 'block-id': 'block-cf49462c-2370-48ff-969d-576cb32c39a1' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-30ef8361-0dd6-4eee-b4eb-c9012d0e9070' } + }, + { + insert: { + slash_command: { + id: 'doc_94zq-2436', + sessionId: 'x9x08o916', + replace: '/' + } + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-166ed856-cf8c-486a-9365-f499b21d91b3' } + }, + { insert: { divider: true } }, + { + insert: '\n', + attributes: { + row: 'row-kssn15', + rowspan: '1', + colspan: '1', + 'block-id': 'block-e8079594-4559-4259-98bb-da5280e2a692', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-kssn15', + cell: 'cell-qxbksf' + }, + cell: 'cell-qxbksf' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-70132663-14cc-4701-b5c5-eb99e875e2bd', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-kssn15', + cell: 'cell-lsohbx' + }, + cell: 'cell-lsohbx', + row: 'row-kssn15', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-47a3899c-e3c5-4a7a-a8c4-46e0ae73a4fa', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-kssn15', + cell: 'cell-hner9k' + }, + cell: 'cell-hner9k', + row: 'row-kssn15', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-0f9e650a-7841-412e-b4f2-5571b6d352c2', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-juxwc0', + cell: 'cell-ei4yqp' + }, + cell: 'cell-ei4yqp', + row: 'row-juxwc0', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-53a158a9-8c82-4c82-9d4e-f5298257ca43', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-juxwc0', + cell: 'cell-25pf5x' + }, + cell: 'cell-25pf5x', + row: 'row-juxwc0', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-da8ba35e-ce6e-4518-8605-c51d781eb07a', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-juxwc0', + cell: 'cell-m8reor' + }, + cell: 'cell-m8reor', + row: 'row-juxwc0', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-2dce37c7-2978-4127-bed0-9549781babcb', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ot4wy5', + cell: 'cell-dinh0i' + }, + cell: 'cell-dinh0i', + row: 'row-ot4wy5', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-7b593f8c-4ea3-44b4-8ad9-4a0abffe759b', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ot4wy5', + cell: 'cell-d115b2' + }, + cell: 'cell-d115b2', + row: 'row-ot4wy5', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-272c28e6-2bde-4477-9d99-ce35b3045895', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ot4wy5', + cell: 'cell-fuapvo' + }, + cell: 'cell-fuapvo', + row: 'row-ot4wy5', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-fbf23cab-1ce9-4ede-9953-f2f8250004cf' } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-c3fbb8c9-495c-40b0-b0dd-f6e33dd64b1b' } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-3417ad09-92a3-4a43-b5db-6dbcb0f16db4' } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-b9eacdce-4ba3-4e66-8b69-3eace5656057' } + }, + { insert: 'Dan Gornstein' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d7c6ae0d-a17c-433e-85fd-5efc52b587fb', + header: 1 + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-814521bd-0e14-4fbf-b332-799c6452a624' } + }, + { insert: 'aaa' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-6aaf4dcf-dc21-45c6-b723-afb25fe0f498', + list: { list: 'toggled', 'toggle-id': 'list-idl93b' } + } + }, + { insert: 'bb' }, + { + insert: '\n', + attributes: { + indent: 1, + 'block-id': 'block-3dd75392-fa50-4bfb-ba6b-3b7d6bd3f1a1', + list: { list: 'toggled', 'toggle-id': 'list-mrq7j2' } + } + }, + { insert: 'ccc' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-2528578b-ecda-4f74-9fd7-8741d72dc8b3', + indent: 2, + list: { list: 'toggled', 'toggle-id': 'list-liu7dl' } + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-18bf68c3-9ef3-4874-929c-9b6bb1a00325' } + }, + { + insert: '\n', + attributes: { 'table-col': { width: '150' } } + }, + { insert: '\n', attributes: { 'table-col': { width: '150' } } }, + { + insert: '\n', + attributes: { 'table-col': { width: '150' } } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d44e74b4-b37f-48e0-b319-6327a6295a57', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + }, + row: 'row-si1nah', + cell: 'cell-cpybie', + rowspan: '1', + colspan: '1' + } + }, + { insert: 'aaa' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-3e545ee9-0c9a-42d7-a4d0-833edb8087f3', + list: { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie', + list: 'toggled', + 'toggle-id': 'list-kjl2ik' + }, + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + } + }, + { insert: 'bb' }, + { + insert: '\n', + attributes: { + indent: 1, + 'block-id': 'block-5f1225ad-370f-46ab-8f1e-18b277b5095f', + list: { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie', + list: 'toggled', + 'toggle-id': 'list-eei1x5' + }, + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + } + }, + { insert: 'ccc' }, + { + insert: '\n', + attributes: { + indent: 2, + 'block-id': 'block-a77fdc11-ad24-431b-9ca2-09e32db94ac2', + list: { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie', + list: 'toggled', + 'toggle-id': 'list-30us3c' + }, + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d44e74b4-b37f-48e0-b319-6327a6295a57', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + }, + row: 'row-si1nah', + cell: 'cell-cpybie', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-2c274c8a-757d-4892-8db8-1a7999f7ab51', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-al1z64' + }, + row: 'row-si1nah', + cell: 'cell-al1z64', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-85931afe-1879-471c-bb4b-89e7bd517fe9', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-q186pb' + }, + row: 'row-si1nah', + cell: 'cell-q186pb', + rowspan: '1', + colspan: '1' + } + }, + { insert: 'asdfasdfasdf' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-6e0522e8-c1eb-4c07-98df-2b07c533a139', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-7x2d1o', + cell: 'cell-6eid2t' + }, + row: 'row-7x2d1o', + cell: 'cell-6eid2t', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-4b3d0bd0-9175-45e9-955c-e8164f4b5376', + row: 'row-7x2d1o', + cell: 'cell-m1alad', + rowspan: '1', + colspan: '1', + list: { + rowspan: '1', + colspan: '1', + row: 'row-7x2d1o', + cell: 'cell-m1alad', + list: 'ordered' + } + } + }, + { insert: 'asdfasdfasdf' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-08610089-cb05-4366-bb1e-a0787d5b11bf', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-7x2d1o', + cell: 'cell-dm1l2p' + }, + row: 'row-7x2d1o', + cell: 'cell-dm1l2p', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-c22b5125-8df3-432f-bd55-5ff456e41b4e', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-o0ujua', + cell: 'cell-82g0ca' + }, + row: 'row-o0ujua', + cell: 'cell-82g0ca', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-7c6320e4-acaf-4ab4-8355-c9b00408c9c1', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-o0ujua', + cell: 'cell-wv6ozp' + }, + row: 'row-o0ujua', + cell: 'cell-wv6ozp', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-d1bb7bed-e69e-4807-8d20-2d28fef8d08f', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-o0ujua', + cell: 'cell-ldt53x' + }, + row: 'row-o0ujua', + cell: 'cell-ldt53x', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-28f28cb8-51a2-4156-acf9-2380e1349745' } + }, + { insert: { divider: true } }, + { + insert: '\n', + attributes: { 'block-id': 'block-a1193252-c0c8-47fe-b9f6-32c8b01a1619' } + }, + { insert: '\n', attributes: { 'table-col': { width: '150' } } }, + { + insert: '\n\n', + attributes: { 'table-col': { width: '150' } } + }, + { insert: '/This is a test.' }, + { + insert: '\n', + attributes: { + 'block-id': 'block-14188df0-a63f-4317-9a6d-91b96a7ac9fe', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-5ixdvv', + cell: 'cell-9tgyed' + }, + row: 'row-5ixdvv', + cell: 'cell-9tgyed', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-7e5ba2af-9903-457d-adf4-2a79be81d823', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-5ixdvv', + cell: 'cell-xc56e9' + }, + row: 'row-5ixdvv', + cell: 'cell-xc56e9', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-eb6cad93-caf7-4848-8adf-415255139268', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-5ixdvv', + cell: 'cell-xrze3u' + }, + row: 'row-5ixdvv', + cell: 'cell-xrze3u', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-5bb547a2-6f71-4624-80c7-d0e1318c81a2', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-xbzv98', + cell: 'cell-lie0ng' + }, + row: 'row-xbzv98', + cell: 'cell-lie0ng', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-b506de0d-efb6-4bd7-ba8e-2186cc57903e', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-xbzv98', + cell: 'cell-s9sow1' + }, + row: 'row-xbzv98', + cell: 'cell-s9sow1', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-42d2ad20-5521-40e3-a88d-fe6906176e61', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-xbzv98', + cell: 'cell-nodtcj' + }, + row: 'row-xbzv98', + cell: 'cell-nodtcj', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-7d3e4216-3f68-4dd6-bc77-4a9fad4ba008', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-5bqfil', + cell: 'cell-c8c0f3' + }, + row: 'row-5bqfil', + cell: 'cell-c8c0f3', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-6671f221-551e-47fb-9b7d-9043b6b12cdc', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-5bqfil', + cell: 'cell-jvxxif' + }, + row: 'row-5bqfil', + cell: 'cell-jvxxif', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { + 'block-id': 'block-51e3161b-0437-4fe3-ac4f-129a93a93fc3', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-5bqfil', + cell: 'cell-rmjpze' + }, + row: 'row-5bqfil', + cell: 'cell-rmjpze', + rowspan: '1', + colspan: '1' + } + }, + { + insert: '\n', + attributes: { 'block-id': 'block-21099df0-afb2-4cd3-834d-bb37800eb06a' } + } + ] + const ydoc = new Y.Doc() + const ytext = ydoc.getText('id') + ytext.applyDelta(initialContent) + const changeEvent = [ + { retain: 90 }, + { delete: 4 }, + { + retain: 1, + attributes: { + layout: null, + 'layout-width': null, + 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' + } + } + ] + ytext.applyDelta(changeEvent) + const delta = ytext.toDelta() + t.compare(delta[41], { + insert: '\n', + attributes: { + 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' + } + }) +} + /** * In this test we are mainly interested in the cleanup behavior and whether the resulting delta makes sense. * It is fine if the resulting delta is not minimal. But applying the delta to a rich-text editor should result in a From 035e350062c29f461e584c0f3f4ce5449c974081 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 2 Mar 2023 19:48:00 +0100 Subject: [PATCH 030/362] optimize formatting cleanup --- src/types/YText.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 83b02aa63..68a052c14 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -363,12 +363,18 @@ const formatText = (transaction, parent, currPos, length, attributes) => { * @function */ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => { - let end = curr - const endAttributes = map.copy(currAttributes) + /** + * @type {Item|null} + */ + let end = start + /** + * @type {Map} + */ + const endFormats = map.create() while (end && (!end.countable || end.deleted)) { if (!end.deleted && end.content.constructor === ContentFormat) { - // @todo should set endAttributes[end.key] = end and then compare identities instead of values - updateCurrentAttributes(endAttributes, /** @type {ContentFormat} */ (end.content)) + const cf = /** @type {ContentFormat} */ (end.content) + endFormats.set(cf.key, cf) } end = end.right } @@ -384,7 +390,7 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (content) const startAttrValue = startAttributes.get(key) || null - if ((endAttributes.get(key) || null) !== value || startAttrValue === value) { + if (endFormats.get(key) !== content || startAttrValue === value) { // Either this format is overwritten or it is not necessary because the attribute already existed. start.delete(transaction) cleanups++ From 7e40fc442d4b2f3e527de20fef40ddddc0ee3caf Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 2 Mar 2023 19:50:34 +0100 Subject: [PATCH 031/362] 13.5.48 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50c0584c9..2a2b9a586 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.47", + "version": "13.5.48", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.47", + "version": "13.5.48", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index 77272de79..d5468aa96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.47", + "version": "13.5.48", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From fe48efe64f07b9d98aecc63c2234230d2fb1a76a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 9 Mar 2023 13:44:52 +0100 Subject: [PATCH 032/362] fix generating too many cleanup transactions. closes #506 --- src/types/YXmlFragment.js | 3 ++- src/utils/Transaction.js | 11 +++++++++-- tests/doc.tests.js | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index b229a4acc..06b1c0de6 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -257,7 +257,8 @@ export class YXmlFragment extends AbstractType { * @return {string} The string representation of all children. */ toString () { - return typeListMap(this, xml => xml.toString()).join('') + // toString can result in many cleanup transactions. We wrap all cleanup transactions here to reduce the work + return transact(/** @type {Doc} */ (this.doc), () => typeListMap(this, xml => xml.toString()).join('')) } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index f6a1179a6..1e553a902 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -376,15 +376,21 @@ const cleanupTransactions = (transactionCleanups, i) => { /** * Implements the functionality of `y.transact(()=>{..})` * + * @template T * @param {Doc} doc - * @param {function(Transaction):void} f + * @param {function(Transaction):T} f * @param {any} [origin=true] + * @return {T} * * @function */ export const transact = (doc, f, origin = null, local = true) => { const transactionCleanups = doc._transactionCleanups let initialCall = false + /** + * @type {any} + */ + let result = null if (doc._transaction === null) { initialCall = true doc._transaction = new Transaction(doc, origin, local) @@ -395,7 +401,7 @@ export const transact = (doc, f, origin = null, local = true) => { doc.emit('beforeTransaction', [doc._transaction, doc]) } try { - f(doc._transaction) + result = f(doc._transaction) } finally { if (initialCall) { const finishCleanup = doc._transaction === transactionCleanups[0] @@ -413,4 +419,5 @@ export const transact = (doc, f, origin = null, local = true) => { } } } + return result } diff --git a/tests/doc.tests.js b/tests/doc.tests.js index c62119776..e2910a1f5 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -2,6 +2,24 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' +/** + * @param {t.TestCase} _tc + */ +export const testAfterTransactionRecursion = _tc => { + const ydoc = new Y.Doc() + const yxml = ydoc.getXmlFragment('') + ydoc.on('afterTransaction', tr => { + if (tr.origin === 'test') { + yxml.toJSON() + } + }) + ydoc.transact(_tr => { + for (let i = 0; i < 15000; i++) { + yxml.push([new Y.XmlText('a')]) + } + }, 'test') +} + /** * @param {t.TestCase} _tc */ From 52ff230dd1bf96b0563264bd7b47732097deea35 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 9 Mar 2023 13:59:08 +0100 Subject: [PATCH 033/362] 13.5.49 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a2b9a586..bede731f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.48", + "version": "13.5.49", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.48", + "version": "13.5.49", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index d5468aa96..2a3bf63d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.48", + "version": "13.5.49", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 92bad631451c7ee28bf0bc61b55d45fa743bc08a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 9 Mar 2023 18:44:43 +0100 Subject: [PATCH 034/362] add docs: tr.changes should only be computed during the event --- src/utils/YEvent.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 5d33ef676..ac877bc51 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -130,6 +130,11 @@ export class YEvent { } /** + * This is a computed property. Note that this can only be safely computed during the + * event call. Computing this property after other changes happened might result in + * unexpected behavior (incorrect computation of deltas). A safe way to collect changes + * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. + * * @type {Array<{insert?: string | Array | object | AbstractType, retain?: number, delete?: number, attributes?: Object}>} */ get delta () { @@ -149,6 +154,11 @@ export class YEvent { } /** + * This is a computed property. Note that this can only be safely computed during the + * event call. Computing this property after other changes happened might result in + * unexpected behavior (incorrect computation of deltas). A safe way to collect changes + * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. + * * @type {{added:Set,deleted:Set,keys:Map,delta:Array<{insert?:Array|string, delete?:number, retain?:number}>}} */ get changes () { From da8bacfc78dff7321f685a60a00df81b64698b3b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 10 Mar 2023 12:53:48 +0100 Subject: [PATCH 035/362] add tests for complex Y.Text deltas --- tests/y-text.tests.js | 67 ++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index cd44142fb..87ac21a4a 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1747,9 +1747,9 @@ export const testBasicFormat = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testMultilineFormat = tc => { +export const testMultilineFormat = _tc => { const ydoc = new Y.Doc() const testText = ydoc.getText('test') testText.insert(0, 'Test\nMulti-line\nFormatting') @@ -1770,9 +1770,9 @@ export const testMultilineFormat = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testNotMergeEmptyLinesFormat = tc => { +export const testNotMergeEmptyLinesFormat = _tc => { const ydoc = new Y.Doc() const testText = ydoc.getText('test') testText.applyDelta([ @@ -1790,9 +1790,9 @@ export const testNotMergeEmptyLinesFormat = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testPreserveAttributesThroughDelete = tc => { +export const testPreserveAttributesThroughDelete = _tc => { const ydoc = new Y.Doc() const testText = ydoc.getText('test') testText.applyDelta([ @@ -2046,9 +2046,9 @@ const id = Y.createID(0, 0) const c = new Y.ContentString('a') /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testBestCase = tc => { +export const testBestCase = _tc => { const N = largeDocumentSize const items = new Array(N) t.measureTime('time to create two million items in the best case', () => { @@ -2085,9 +2085,9 @@ const tryGc = () => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testLargeFragmentedDocument = tc => { +export const testLargeFragmentedDocument = _tc => { const itemsToInsert = largeDocumentSize let update = /** @type {any} */ (null) ;(() => { @@ -2117,9 +2117,9 @@ export const testLargeFragmentedDocument = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testIncrementalUpdatesPerformanceOnLargeFragmentedDocument = tc => { +export const testIncrementalUpdatesPerformanceOnLargeFragmentedDocument = _tc => { const itemsToInsert = largeDocumentSize const updates = /** @type {Array} */ ([]) ;(() => { @@ -2227,9 +2227,9 @@ export const testSearchMarkerBug1 = tc => { /** * Reported in https://github.com/yjs/yjs/pull/32 * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testFormattingBug = async tc => { +export const testFormattingBug = async _tc => { const ydoc1 = new Y.Doc() const ydoc2 = new Y.Doc() const text1 = ydoc1.getText() @@ -2252,9 +2252,9 @@ export const testFormattingBug = async tc => { /** * Delete formatting should not leave redundant formatting items. * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testDeleteFormatting = tc => { +export const testDeleteFormatting = _tc => { const doc = new Y.Doc() const text = doc.getText() text.insert(0, 'Attack ships on fire off the shoulder of Orion.') @@ -2456,6 +2456,41 @@ const qChanges = [ } ops.push({ insert: text }, { insert: '\n', format: { 'code-block': true } }) ytext.applyDelta(ops) + }, + /** + * @param {Y.Doc} y + * @param {prng.PRNG} gen + */ + (y, gen) => { // complex delta op + const ytext = y.getText('text') + const contentLen = ytext.toString().length + let currentPos = math.max(0, prng.int32(gen, 0, contentLen - 1)) + /** + * @type {Array} + */ + const ops = currentPos > 0 ? [{ retain: currentPos }] : [] + // create max 3 ops + for (let i = 0; i < 7 && currentPos < contentLen; i++) { + prng.oneOf(gen, [ + () => { // format + const retain = math.min(prng.int32(gen, 0, contentLen - currentPos), 5) + const format = prng.oneOf(gen, marks) + ops.push({ retain, attributes: format }) + currentPos += retain + }, + () => { // insert + const attrs = prng.oneOf(gen, marksChoices) + const text = prng.word(gen, 1, 3) + ops.push({ insert: text, attributes: attrs }) + }, + () => { // delete + const delLen = math.min(prng.int32(gen, 0, contentLen - currentPos), 10) + ops.push({ delete: delLen }) + currentPos += delLen + } + ])() + } + ytext.applyDelta(ops) } ] From 227018f5c77b67967f67810ade40cbb190cbc2c8 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 11 Mar 2023 09:13:27 +0100 Subject: [PATCH 036/362] toDelta doesnt create transaction - fixes #506 --- src/types/YText.js | 27 +++++++++++++++++---------- src/types/YXmlFragment.js | 3 +-- src/utils/Doc.js | 6 ++++-- tests/doc.tests.js | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 68a052c14..6f05b6d0d 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1018,15 +1018,7 @@ export class YText extends AbstractType { str = '' } } - // snapshots are merged again after the transaction, so we need to keep the - // transalive until we are done - transact(doc, transaction => { - if (snapshot) { - splitSnapshotAffectedStructs(transaction, snapshot) - } - if (prevSnapshot) { - splitSnapshotAffectedStructs(transaction, prevSnapshot) - } + const computeDelta = () => { while (n !== null) { if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) { switch (n.content.constructor) { @@ -1079,7 +1071,22 @@ export class YText extends AbstractType { n = n.right } packStr() - }, 'cleanup') + } + if (snapshot || prevSnapshot) { + // snapshots are merged again after the transaction, so we need to keep the + // transaction alive until we are done + transact(doc, transaction => { + if (snapshot) { + splitSnapshotAffectedStructs(transaction, snapshot) + } + if (prevSnapshot) { + splitSnapshotAffectedStructs(transaction, prevSnapshot) + } + computeDelta() + }, 'cleanup') + } else { + computeDelta() + } return ops } diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 06b1c0de6..b229a4acc 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -257,8 +257,7 @@ export class YXmlFragment extends AbstractType { * @return {string} The string representation of all children. */ toString () { - // toString can result in many cleanup transactions. We wrap all cleanup transactions here to reduce the work - return transact(/** @type {Doc} */ (this.doc), () => typeListMap(this, xml => xml.toString()).join('')) + return typeListMap(this, xml => xml.toString()).join('') } /** diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 9588d3621..d07dd56ee 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -156,13 +156,15 @@ export class Doc extends Observable { * that happened inside of the transaction are sent as one message to the * other peers. * - * @param {function(Transaction):void} f The function that should be executed as a transaction + * @template T + * @param {function(Transaction):T} f The function that should be executed as a transaction * @param {any} [origin] Origin of who started the transaction. Will be stored on transaction.origin + * @return T * * @public */ transact (f, origin = null) { - transact(this, f, origin) + return transact(this, f, origin) } /** diff --git a/tests/doc.tests.js b/tests/doc.tests.js index e2910a1f5..605249360 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -33,7 +33,7 @@ export const testOriginInTransaction = _tc => { doc.on('afterTransaction', (tr) => { origins.push(tr.origin) if (origins.length <= 1) { - ytext.toDelta() + ytext.toDelta(Y.snapshot(doc)) // adding a snapshot forces toDelta to create a cleanup transaction doc.transact(() => { ytext.insert(0, 'a') }, 'nested') From 2e2710ded939cca0ead6e6be19dc418c193bdaf8 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 11 Mar 2023 09:15:11 +0100 Subject: [PATCH 037/362] 13.5.50 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bede731f9..49f13697c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.49", + "version": "13.5.50", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.49", + "version": "13.5.50", "license": "MIT", "dependencies": { "lib0": "^0.2.49" diff --git a/package.json b/package.json index 2a3bf63d4..2910017a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.49", + "version": "13.5.50", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2001bec8ebb445ef6287a42e8f8dfde1ad32e5a9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 11 Mar 2023 12:20:52 +0100 Subject: [PATCH 038/362] modernize tsconfig --- tests/testHelper.js | 12 ++++---- tsconfig.json | 69 +++++++++------------------------------------ 2 files changed, 19 insertions(+), 62 deletions(-) diff --git a/tests/testHelper.js b/tests/testHelper.js index f571880ed..7bd0d45f2 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -347,7 +347,7 @@ export const compare = users => { t.compare(userMapValues[i], userMapValues[i + 1]) t.compare(userXmlValues[i], userXmlValues[i + 1]) t.compare(userTextValues[i].map(/** @param {any} a */ a => typeof a.insert === 'string' ? a.insert : ' ').join('').length, users[i].getText('text').length) - t.compare(userTextValues[i], userTextValues[i + 1], '', (constructor, a, b) => { + t.compare(userTextValues[i], userTextValues[i + 1], '', (_constructor, a, b) => { if (a instanceof Y.AbstractType) { t.compare(a.toJSON(), b.toJSON()) } else if (a !== b) { @@ -370,8 +370,8 @@ export const compare = users => { export const compareItemIDs = (a, b) => a === b || (a !== null && b != null && Y.compareIDs(a.id, b.id)) /** - * @param {import('../src/internals').StructStore} ss1 - * @param {import('../src/internals').StructStore} ss2 + * @param {import('../src/internals.js').StructStore} ss1 + * @param {import('../src/internals.js').StructStore} ss2 */ export const compareStructStores = (ss1, ss2) => { t.assert(ss1.clients.size === ss2.clients.size) @@ -413,13 +413,13 @@ export const compareStructStores = (ss1, ss2) => { } /** - * @param {import('../src/internals').DeleteSet} ds1 - * @param {import('../src/internals').DeleteSet} ds2 + * @param {import('../src/internals.js').DeleteSet} ds1 + * @param {import('../src/internals.js').DeleteSet} ds2 */ export const compareDS = (ds1, ds2) => { t.assert(ds1.clients.size === ds2.clients.size) ds1.clients.forEach((deleteItems1, client) => { - const deleteItems2 = /** @type {Array} */ (ds2.clients.get(client)) + const deleteItems2 = /** @type {Array} */ (ds2.clients.get(client)) t.assert(deleteItems2 !== undefined && deleteItems1.length === deleteItems2.length) for (let i = 0; i < deleteItems1.length; i++) { const di1 = deleteItems1[i] diff --git a/tsconfig.json b/tsconfig.json index 31ccd70ee..2520c4048 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,64 +1,21 @@ { "compilerOptions": { - /* Basic Options */ - "target": "es2018", - "lib": ["es2018", "dom"], /* Specify library files to be included in the compilation. */ - "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. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "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. */ - // "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'). */ - - /* 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. */ + "target": "ES2021", + "lib": ["ES2021", "dom"], + "module": "node16", + "allowJs": true, + "checkJs": true, + "declaration": true, + "declarationMap": true, + "outDir": "./dist", + "baseUrl": "./", "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. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + "strict": true, + "noImplicitAny": true, + "moduleResolution": "nodenext", "paths": { "yjs": ["./src/index.js"] - }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - // "maxNodeModuleJsDepth": 0, - // "types": ["./src/utils/typedefs.js"] + } }, "include": ["./src/**/*.js", "./tests/**/*.js"] } From cb70d7bad379374b966445038b37b0faa313d856 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Mar 2023 11:14:37 +0100 Subject: [PATCH 039/362] fix typings and lib0 resolution --- package-lock.json | 3494 +++------------------------------------- package.json | 9 +- rollup.config.js | 7 +- src/utils/DeleteSet.js | 4 +- tests/index.js | 1 + tests/testHelper.js | 2 +- 6 files changed, 234 insertions(+), 3283 deletions(-) diff --git a/package-lock.json b/package-lock.json index 49f13697c..e83f82be9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "yjs", "version": "13.5.50", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -12,13 +12,14 @@ "lib0": "^0.2.49" }, "devDependencies": { - "@rollup/plugin-commonjs": "^17.0.0", - "@rollup/plugin-node-resolve": "^11.2.1", + "@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.23.2", - "rollup": "^2.60.0", + "rollup": "^3.20.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", "typescript": "^4.9.5", @@ -65,9 +66,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", - "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", + "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -129,74 +130,128 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@rollup/plugin-commonjs": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", - "integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==", + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz", + "integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.1.0", + "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.27.0" }, "engines": { - "node": ">= 8.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^2.30.0" + "rollup": "^2.68.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", + "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.0", "is-module": "^1.0.0", - "resolve": "^1.19.0" + "resolve": "^1.22.1" }, "engines": { - "node": ">= 10.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "rollup": "^2.78.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, "node_modules/@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", "dev": true, "dependencies": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">= 8.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, "node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, "node_modules/@types/json5": { @@ -228,19 +283,16 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", + "version": "18.15.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz", + "integrity": "sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==", "dev": true }, "node_modules/@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "dependencies": { - "@types/node": "*" - } + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true }, "node_modules/acorn": { "version": "7.4.1", @@ -315,6 +367,19 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array-includes": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", @@ -696,18 +761,18 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", "dev": true, "dependencies": { "has-property-descriptors": "^1.0.0", @@ -826,18 +891,18 @@ } }, "node_modules/es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", "dev": true, "dependencies": { + "array-buffer-byte-length": "^1.0.0", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -845,8 +910,8 @@ "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", @@ -854,11 +919,12 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.10", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", + "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.4.3", "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", "typed-array-length": "^1.0.4", @@ -1434,9 +1500,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -1750,9 +1816,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/graceful-readlink": { @@ -1973,12 +2039,12 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "has": "^1.0.3", "side-channel": "^1.0.4" }, @@ -1987,13 +2053,13 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "get-intrinsic": "^1.2.0", "is-typed-array": "^1.1.10" }, "funding": { @@ -2034,6 +2100,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -2396,12 +2477,16 @@ } }, "node_modules/lib0": { - "version": "0.2.60", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.60.tgz", - "integrity": "sha512-vzhtdUXBV8HyJnJWIZxUSH/aUVo1U4jUFRFDPVY245zFtzCl9Gld/EgvA8Jhnrio7Jn0HmGswErbPjsabYd7ow==", + "version": "0.2.70", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.70.tgz", + "integrity": "sha512-RpUODIS+AqQjoK4XrgMDZkox3S2pxzvnt5d0itCXCWd8NW7flboVAvvIbNUWR6XLTJz8I7FTPcNu1UFhvs0MsA==", "dependencies": { "isomorphic.js": "^0.2.4" }, + "bin": { + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, "engines": { "node": ">=14" }, @@ -2562,12 +2647,15 @@ } }, "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", "dev": true, "dependencies": { - "sourcemap-codec": "^1.4.8" + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" } }, "node_modules/markdown-it": { @@ -2587,9 +2675,9 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.6.6", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", - "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", @@ -2769,9 +2857,9 @@ } }, "node_modules/minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3272,9 +3360,9 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -3353,9 +3441,9 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -3461,15 +3549,16 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.0.tgz", + "integrity": "sha512-YsIfrk80NqUDrxrjWPXUa7PWvAfegZEXHuPsEZg58fGCdjL1I9C1i/NaG+L+27kxxwkrG/QEDEQc8s/ynXWWGQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.18.0", + "npm": ">=8.0.0" }, "optionalDependencies": { "fsevents": "~2.3.2" @@ -3615,13 +3704,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, "node_modules/spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", @@ -3629,9 +3711,9 @@ "dev": true }, "node_modules/spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -3655,9 +3737,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", "dev": true }, "node_modules/sprintf-js": { @@ -3785,6 +3867,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -3930,13 +4029,13 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", - "json5": "^1.0.1", + "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } @@ -4174,3156 +4273,5 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz", - "integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@rollup/plugin-commonjs": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", - "integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" - } - }, - "@rollup/plugin-node-resolve": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", - "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "builtin-modules": "^3.1.0", - "deepmerge": "^4.2.2", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "dependencies": { - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - } - } - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, - "@types/node": { - "version": "18.11.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", - "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", - "dev": true - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "basic-auth": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", - "dev": true, - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - } - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "commander": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "concurrently": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.1.tgz", - "integrity": "sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "commander": "2.6.0", - "date-fns": "^1.23.0", - "lodash": "^4.5.1", - "read-pkg": "^3.0.0", - "rx": "2.3.24", - "spawn-command": "^0.0.2-1", - "supports-color": "^3.2.3", - "tree-kill": "^1.1.0" - } - }, - "corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", - "dev": true, - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "dev": true - }, - "date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", - "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz", - "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==", - "dev": true - }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "dev": true, - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", - "dev": true, - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "ecstatic": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", - "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", - "dev": true, - "requires": { - "he": "^1.1.1", - "mime": "^1.6.0", - "minimist": "^1.1.0", - "url-join": "^2.0.5" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", - "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.1.3", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.4", - "is-array-buffer": "^3.0.1", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.2", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", - "dev": true, - "requires": {} - }, - "eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", - "dev": true, - "requires": {} - }, - "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", - "dev": true, - "requires": { - "debug": "^3.2.7" - } - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - } - }, - "eslint-plugin-import": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.6.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.11.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-promise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", - "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", - "dev": true, - "requires": {} - }, - "eslint-plugin-react": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", - "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", - "dev": true, - "requires": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "estraverse": "^5.2.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.hasown": "^1.0.0", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - } - } - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-server": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", - "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", - "dev": true, - "requires": { - "basic-auth": "^1.0.3", - "colors": "^1.4.0", - "corser": "^2.0.1", - "ecstatic": "^3.3.2", - "http-proxy": "^1.18.0", - "minimist": "^1.2.5", - "opener": "^1.5.1", - "portfinder": "^1.0.25", - "secure-compare": "3.0.1", - "union": "~0.5.0" - } - }, - "ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "internal-slot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", - "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-array-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", - "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-typed-array": "^1.1.10" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isomorphic.js": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", - "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - } - } - }, - "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "requires": { - "xmlcreate": "^2.0.4" - } - }, - "jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonc-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", - "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==", - "dev": true - }, - "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dev": true, - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lib0": { - "version": "0.2.60", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.60.tgz", - "integrity": "sha512-vzhtdUXBV8HyJnJWIZxUSH/aUVo1U4jUFRFDPVY245zFtzCl9Gld/EgvA8Jhnrio7Jn0HmGswErbPjsabYd7ow==", - "requires": { - "isomorphic.js": "^0.2.4" - } - }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", - "dev": true - }, - "lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==", - "dev": true - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true - }, - "lodash.differencewith": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", - "integrity": "sha512-/8JFjydAS+4bQuo3CpLMBv7WxGFyk7/etOAsrQUCu0a9QVDemxv0YQ0rFyeZvqlUD314SERfNlgnlqqHmaQ0Cg==", - "dev": true - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==", - "dev": true - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", - "dev": true - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "dev": true - }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", - "dev": true - }, - "lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==", - "dev": true - }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - }, - "markdown-it-anchor": { - "version": "8.6.6", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", - "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", - "dev": true, - "requires": {} - }, - "markdownlint": { - "version": "0.20.4", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.20.4.tgz", - "integrity": "sha512-jpfaPgjT0OpeBbemjYNZbzGG3hCLcAIvrm/pEY3+q/szDScG6ZonDacqySVRJAv9glbo8y4wBPJ0wgW17+9GGA==", - "dev": true, - "requires": { - "markdown-it": "10.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", - "dev": true - }, - "linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", - "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - } - } - }, - "markdownlint-cli": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.23.2.tgz", - "integrity": "sha512-OSl5OZ8xzGN6z355cqRkiq67zPi3reJimklaF72p0554q85Dng5ToOjjSB9tDKZebSt85jX8cp+ruoQlPqOsPA==", - "dev": true, - "requires": { - "commander": "~2.9.0", - "deep-extend": "~0.5.1", - "get-stdin": "~5.0.1", - "glob": "~7.1.2", - "ignore": "~5.1.4", - "js-yaml": "~3.13.1", - "jsonc-parser": "~2.2.0", - "lodash.differencewith": "~4.5.0", - "lodash.flatten": "~4.4.0", - "markdownlint": "~0.20.4", - "markdownlint-rule-helpers": "~0.11.0", - "minimatch": "~3.0.4", - "minimist": "~1.2.5", - "rc": "~1.2.7" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "markdownlint-rule-helpers": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.11.0.tgz", - "integrity": "sha512-PhGii9dOiDJDXxiRMpK8N0FM9powprvRPsXALgkjlSPTwLh6ymH+iF3iUe3nq8KGu26tclFBlLL5xAGy/zb7FA==", - "dev": true - }, - "marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", - "dev": true - }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", - "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "requires": { - "boolbase": "~1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "dev": true, - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - }, - "pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true - } - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", - "dev": true, - "requires": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "requires": { - "side-channel": "^1.0.4" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "requizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", - "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", - "dev": true, - "requires": { - "lodash": "^4.17.21" - } - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "rx": { - "version": "2.3.24", - "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", - "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", - "dev": true - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spawn-command": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", - "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "standard": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", - "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", - "dev": true, - "requires": { - "eslint": "~7.18.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "~2.24.2", - "eslint-plugin-node": "~11.1.0", - "eslint-plugin-promise": "~5.1.0", - "eslint-plugin-react": "~7.25.1", - "standard-engine": "^14.0.1" - } - }, - "standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", - "dev": true, - "requires": { - "get-stdin": "^8.0.0", - "minimist": "^1.2.5", - "pkg-conf": "^3.1.0", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true - } - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "dev": true, - "requires": { - "has-flag": "^1.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "tsconfig-paths": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", - "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "tui-jsdoc-template": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tui-jsdoc-template/-/tui-jsdoc-template-1.2.2.tgz", - "integrity": "sha512-oqw0IYaot86VJ2owKBozJnilgta0Z55x8r9PeHj7vb+jDoSvJGRUQUcgs56SZh9HE20fx54Pe75p84X85/ygLA==", - "dev": true, - "requires": { - "cheerio": "^0.22.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true - }, - "union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "requires": { - "qs": "^6.4.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha512-c2H1fIgpUdwFRIru9HFno5DT73Ok8hg5oOb5AT3ayIgvCRfxgs2jyt5Slw8kEB7j3QUr6yJmMPDT/odjk7jXow==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, - "y-protocols": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.5.tgz", - "integrity": "sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==", - "dev": true, - "requires": { - "lib0": "^0.2.42" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } } } diff --git a/package.json b/package.json index 2910017a3..15dedefc1 100644 --- a/package.json +++ b/package.json @@ -78,16 +78,17 @@ "lib0": "^0.2.49" }, "devDependencies": { - "@rollup/plugin-commonjs": "^17.0.0", - "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-node-resolve": "^15.0.1", + "@types/node": "^18.15.5", "concurrently": "^3.6.1", - "typescript": "^4.9.5", "http-server": "^0.12.3", "jsdoc": "^3.6.7", "markdownlint-cli": "^0.23.2", - "rollup": "^2.60.0", + "rollup": "^3.20.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", + "typescript": "^4.9.5", "y-protocols": "^1.0.5" } } diff --git a/rollup.config.js b/rollup.config.js index 6bda75660..1978f2a22 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -88,7 +88,7 @@ export default [{ plugins: [ debugResolve, nodeResolve({ - mainFields: ['module', 'browser', 'main'] + mainFields: ['browser', 'module', 'main'] }), commonjs() ] @@ -103,9 +103,10 @@ export default [{ plugins: [ debugResolve, nodeResolve({ - mainFields: ['module', 'main'] + mainFields: ['node', 'module', 'main'], + exportConditions: ['node', 'module', 'import', 'default'] }), commonjs() ], - external: ['isomorphic.js'] + external: id => /^lib0\//.test(id) }] diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 948422742..429c3b913 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -171,7 +171,7 @@ export const mergeDeleteSets = dss => { * @function */ export const addToDeleteSet = (ds, client, clock, length) => { - map.setIfUndefined(ds.clients, client, () => []).push(new DeleteItem(clock, length)) + map.setIfUndefined(ds.clients, client, () => /** @type {Array} */ ([])).push(new DeleteItem(clock, length)) } export const createDeleteSet = () => new DeleteSet() @@ -251,7 +251,7 @@ export const readDeleteSet = decoder => { const client = decoding.readVarUint(decoder.restDecoder) const numberOfDeletes = decoding.readVarUint(decoder.restDecoder) if (numberOfDeletes > 0) { - const dsField = map.setIfUndefined(ds.clients, client, () => []) + const dsField = map.setIfUndefined(ds.clients, client, () => /** @type {Array} */ ([])) for (let i = 0; i < numberOfDeletes; i++) { dsField.push(new DeleteItem(decoder.readDsClock(), decoder.readDsLen())) } diff --git a/tests/index.js b/tests/index.js index 2f8d8874a..ec22ed05b 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,3 +1,4 @@ +/* eslint-env node */ import * as map from './y-map.tests.js' import * as array from './y-array.tests.js' diff --git a/tests/testHelper.js b/tests/testHelper.js index 7bd0d45f2..8c90a099c 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -134,7 +134,7 @@ export class TestYInstance extends Y.Doc { * @param {TestYInstance} remoteClient */ _receive (message, remoteClient) { - map.setIfUndefined(this.receiving, remoteClient, () => []).push(message) + map.setIfUndefined(this.receiving, remoteClient, () => /** @type {Array} */ ([])).push(message) } } From 61ba6cdde1fe55a22479cc435b2e70a11c471087 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Mar 2023 11:22:59 +0100 Subject: [PATCH 040/362] bump ci to use current nodejs versions --- .github/workflows/node.js.yml | 8 +++++--- .github/workflows/nodejs.yml | 31 ------------------------------- 2 files changed, 5 insertions(+), 34 deletions(-) delete mode 100644 .github/workflows/nodejs.yml diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index a391700de..d7aaca982 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [16.x, 18.x] steps: - uses: actions/checkout@v2 @@ -25,5 +25,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: npm ci - - run: npm run build --if-present - - run: npm test + - run: npm run lint + - run: npm run test-extensive + env: + CI: true diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml deleted file mode 100644 index ef026cf1f..000000000 --- a/.github/workflows/nodejs.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Node.js CI - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [10.x, 12.x, 13.x] - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm ci - - run: npm run lint - - run: npm run test-extensive - env: - CI: true From d815855450f5d85f98a5c11b2d21e52c9cae1008 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Mar 2023 11:27:37 +0100 Subject: [PATCH 041/362] specify engine --- package-lock.json | 4 ++++ package.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/package-lock.json b/package-lock.json index e83f82be9..8895aa20e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,10 @@ "typescript": "^4.9.5", "y-protocols": "^1.0.5" }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" diff --git a/package.json b/package.json index 15dedefc1..a09f1e1f0 100644 --- a/package.json +++ b/package.json @@ -90,5 +90,9 @@ "tui-jsdoc-template": "^1.2.2", "typescript": "^4.9.5", "y-protocols": "^1.0.5" + }, + "engines" : { + "npm" : ">=8.0.0", + "node" : ">=16.0.0" } } From e73eb0bf92c34b976a12bae07835bddd3f626348 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 22 Mar 2023 11:02:55 +0100 Subject: [PATCH 042/362] use lib0 conditional exports in cjs file --- package-lock.json | 8 ++++---- package.json | 2 +- rollup.config.js | 8 +------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8895aa20e..f0925ecbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.5.50", "license": "MIT", "dependencies": { - "lib0": "^0.2.49" + "lib0": "^0.2.72" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", @@ -2481,9 +2481,9 @@ } }, "node_modules/lib0": { - "version": "0.2.70", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.70.tgz", - "integrity": "sha512-RpUODIS+AqQjoK4XrgMDZkox3S2pxzvnt5d0itCXCWd8NW7flboVAvvIbNUWR6XLTJz8I7FTPcNu1UFhvs0MsA==", + "version": "0.2.72", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.72.tgz", + "integrity": "sha512-JPnUxl15tO6jrBASQ92+uDyQzW4ISMhDORq6mLovBYxESEWQCj5SnC8oYkELboGbU1ZqCIEEDwCL6mYqWNzdOA==", "dependencies": { "isomorphic.js": "^0.2.4" }, diff --git a/package.json b/package.json index a09f1e1f0..7f5daf098 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.49" + "lib0": "^0.2.72" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", diff --git a/rollup.config.js b/rollup.config.js index 1978f2a22..61afb735e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -42,13 +42,7 @@ export default [{ name: 'Y', file: 'dist/yjs.cjs', format: 'cjs', - sourcemap: true, - paths: path => { - if (/^lib0\//.test(path)) { - return `lib0/dist/${path.slice(5)}.cjs` - } - return path - } + sourcemap: true }, external: id => /^lib0\//.test(id) }, { From 0d7e8655314b62c526bff2520d94c406be2be208 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 22 Mar 2023 11:05:23 +0100 Subject: [PATCH 043/362] 13.5.51 --- package-lock.json | 4 ++-- package.json | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f0925ecbb..68a1163b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.50", + "version": "13.5.51", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.50", + "version": "13.5.51", "license": "MIT", "dependencies": { "lib0": "^0.2.72" diff --git a/package.json b/package.json index 7f5daf098..92fafeaac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.50", + "version": "13.5.51", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", @@ -91,8 +91,8 @@ "typescript": "^4.9.5", "y-protocols": "^1.0.5" }, - "engines" : { - "npm" : ">=8.0.0", - "node" : ">=16.0.0" + "engines": { + "npm": ">=8.0.0", + "node": ">=16.0.0" } } From fb6664a2bcb0feff23231710bfd5d52c030abdd5 Mon Sep 17 00:00:00 2001 From: WofWca Date: Sat, 1 Apr 2023 23:12:49 +0800 Subject: [PATCH 044/362] docs: fix JSDoc typo --- src/types/YArray.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/YArray.js b/src/types/YArray.js index 45b2fb565..a895274e9 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -244,7 +244,7 @@ export class YArray extends AbstractType { } /** - * Executes a provided function on once on overy element of this YArray. + * Executes a provided function once on overy element of this YArray. * * @param {function(T,number,YArray):void} f A function to execute on every element of this YArray. */ From 1674d3986d45ff69a6f7c1b3236ce9ead0c3550b Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Tue, 28 Mar 2023 20:32:22 +0200 Subject: [PATCH 045/362] Restore deleted entries in a map --- src/structs/Item.js | 2 +- tests/undo-redo.tests.js | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/structs/Item.js b/src/structs/Item.js index 704dcd32e..86c05915a 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -200,7 +200,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo } else { right = null if (item.right && !ignoreRemoteMapChanges) { - left = item + left = item.right // Iterate right while right is in itemsToDelete // If it is intended to delete right while item is redone, we can expect that item should replace right. while (left !== null && left.right !== null && isDeleted(itemsToDelete, left.right.id)) { diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 3ee65d18b..261eaffde 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -644,3 +644,43 @@ export const testSpecialDeletionCase = tc => { undoManager.undo() t.compareStrings(fragment.toString(), '') } + +/** + * Deleted entries in a map should be restored on undo. + * + * @see https://github.com/yjs/yjs/issues/500 + * @param {t.TestCase} tc + */ +export const testUndoDeleteInMap = (tc) => { + const { map0 } = init(tc, { users: 3 }) + const undoManager = new Y.UndoManager(map0, { captureTimeout: 0 }) + + map0.set('a', 'a') + map0.delete('a') + map0.set('a', 'b') + map0.delete('a') + map0.set('a', 'c') + map0.delete('a') + map0.set('a', 'd') + + t.compare(map0.toJSON(), { a: 'd' }) + + undoManager.undo() + t.compare(map0.toJSON(), {}) + + undoManager.undo() + t.compare(map0.toJSON(), { a: 'c' }) + + undoManager.undo() + t.compare(map0.toJSON(), {}) + + undoManager.undo() + t.compare(map0.toJSON(), { a: 'b' }) + + undoManager.undo() + t.compare(map0.toJSON(), {}) + + undoManager.undo() + t.compare(map0.toJSON(), { a: 'a' }) +}; + From 99bab4a1d897a62bd2afd4a276df7ac76dce8dd6 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 3 Apr 2023 13:30:36 +0200 Subject: [PATCH 046/362] Fix lint errors --- tests/undo-redo.tests.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 261eaffde..002a05c77 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -647,7 +647,7 @@ export const testSpecialDeletionCase = tc => { /** * Deleted entries in a map should be restored on undo. - * + * * @see https://github.com/yjs/yjs/issues/500 * @param {t.TestCase} tc */ @@ -667,20 +667,19 @@ export const testUndoDeleteInMap = (tc) => { undoManager.undo() t.compare(map0.toJSON(), {}) - + undoManager.undo() t.compare(map0.toJSON(), { a: 'c' }) - + undoManager.undo() t.compare(map0.toJSON(), {}) - + undoManager.undo() t.compare(map0.toJSON(), { a: 'b' }) - + undoManager.undo() t.compare(map0.toJSON(), {}) - + undoManager.undo() t.compare(map0.toJSON(), { a: 'a' }) -}; - +} From ba96f2fe7459a706fae6f60800709622a6c968eb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 3 Apr 2023 14:01:38 +0200 Subject: [PATCH 047/362] implement fix for #500. extends #515 --- package-lock.json | 6 +++--- src/structs/Item.js | 25 +++++++++++++++---------- src/utils/UndoManager.js | 4 ++-- tests/undo-redo.tests.js | 8 -------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 68a1163b6..069407a2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2481,9 +2481,9 @@ } }, "node_modules/lib0": { - "version": "0.2.72", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.72.tgz", - "integrity": "sha512-JPnUxl15tO6jrBASQ92+uDyQzW4ISMhDORq6mLovBYxESEWQCj5SnC8oYkELboGbU1ZqCIEEDwCL6mYqWNzdOA==", + "version": "0.2.73", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.73.tgz", + "integrity": "sha512-aJJIElCLWnHMcYZPtsM07QoSfHwpxCy4VUzBYGXFYEmh/h2QS5uZNbCCfL0CqnkOE30b7Tp9DVfjXag+3qzZjQ==", "dependencies": { "isomorphic.js": "^0.2.4" }, diff --git a/src/structs/Item.js b/src/structs/Item.js index 86c05915a..dd691c21b 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -23,11 +23,12 @@ import { readContentType, addChangedTypeToTransaction, isDeleted, - DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction // eslint-disable-line + StackItem, DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' import * as binary from 'lib0/binary' +import * as array from 'lib0/array' /** * @todo This should return several items @@ -120,6 +121,12 @@ export const splitItem = (transaction, leftItem, diff) => { return rightItem } +/** + * @param {Array} stack + * @param {ID} id + */ +const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => isDeleted(s.deletions, id)) + /** * Redoes the effect of this operation. * @@ -128,12 +135,13 @@ export const splitItem = (transaction, leftItem, diff) => { * @param {Set} redoitems * @param {DeleteSet} itemsToDelete * @param {boolean} ignoreRemoteMapChanges + * @param {import('../utils/UndoManager.js').UndoManager} um * * @return {Item|null} * * @private */ -export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges) => { +export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) => { const doc = transaction.doc const store = doc.store const ownClientID = doc.clientID @@ -153,7 +161,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo // make sure that parent is redone if (parentItem !== null && parentItem.deleted === true) { // try to undo parent if it will be undone anyway - if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges) === null)) { + if (parentItem.redone === null && (!redoitems.has(parentItem) || redoItem(transaction, parentItem, redoitems, itemsToDelete, ignoreRemoteMapChanges, um) === null)) { return null } while (parentItem.redone !== null) { @@ -200,16 +208,13 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo } else { right = null if (item.right && !ignoreRemoteMapChanges) { - left = item.right + left = item // Iterate right while right is in itemsToDelete // If it is intended to delete right while item is redone, we can expect that item should replace right. - while (left !== null && left.right !== null && isDeleted(itemsToDelete, left.right.id)) { + while (left !== null && left.right !== null && (left.right.redone || isDeleted(itemsToDelete, left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) { left = left.right - } - // follow redone - // trace redone until parent matches - while (left !== null && left.redone !== null) { - left = getItemCleanStart(transaction, left.redone) + // follow redone + while (left.redone) left = getItemCleanStart(transaction, left.redone) } if (left && left.right !== null) { // It is not possible to redo this item because it conflicts with a diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 5293cb963..c42fba718 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -17,7 +17,7 @@ import * as time from 'lib0/time' import * as array from 'lib0/array' import { Observable } from 'lib0/observable' -class StackItem { +export class StackItem { /** * @param {DeleteSet} deletions * @param {DeleteSet} insertions @@ -101,7 +101,7 @@ const popStackItem = (undoManager, stack, eventType) => { } }) itemsToRedo.forEach(struct => { - performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.insertions, undoManager.ignoreRemoteMapChanges) !== null || performedChange + performedChange = redoItem(transaction, struct, itemsToRedo, stackItem.insertions, undoManager.ignoreRemoteMapChanges, undoManager) !== null || performedChange }) // We want to delete in reverse order so that children are deleted before // parents, so we have more information available when items are filtered. diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 002a05c77..1c6cecf97 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -654,7 +654,6 @@ export const testSpecialDeletionCase = tc => { export const testUndoDeleteInMap = (tc) => { const { map0 } = init(tc, { users: 3 }) const undoManager = new Y.UndoManager(map0, { captureTimeout: 0 }) - map0.set('a', 'a') map0.delete('a') map0.set('a', 'b') @@ -662,24 +661,17 @@ export const testUndoDeleteInMap = (tc) => { map0.set('a', 'c') map0.delete('a') map0.set('a', 'd') - t.compare(map0.toJSON(), { a: 'd' }) - undoManager.undo() t.compare(map0.toJSON(), {}) - undoManager.undo() t.compare(map0.toJSON(), { a: 'c' }) - undoManager.undo() t.compare(map0.toJSON(), {}) - undoManager.undo() t.compare(map0.toJSON(), { a: 'b' }) - undoManager.undo() t.compare(map0.toJSON(), {}) - undoManager.undo() t.compare(map0.toJSON(), { a: 'a' }) } From 49f435284f6000f94497be3cdaa074434b56e8b6 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 3 Apr 2023 14:10:26 +0200 Subject: [PATCH 048/362] lint --- src/structs/Item.js | 28 +++++++++++++-------------- src/utils/UndoManager.js | 4 ++-- tests/undo-redo.tests.js | 42 ++++++++++++++++++++-------------------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/structs/Item.js b/src/structs/Item.js index dd691c21b..7e1bc92cf 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -761,48 +761,48 @@ export class AbstractContent { } /** - * @param {number} offset + * @param {number} _offset * @return {AbstractContent} */ - splice (offset) { + splice (_offset) { throw error.methodUnimplemented() } /** - * @param {AbstractContent} right + * @param {AbstractContent} _right * @return {boolean} */ - mergeWith (right) { + mergeWith (_right) { throw error.methodUnimplemented() } /** - * @param {Transaction} transaction - * @param {Item} item + * @param {Transaction} _transaction + * @param {Item} _item */ - integrate (transaction, item) { + integrate (_transaction, _item) { throw error.methodUnimplemented() } /** - * @param {Transaction} transaction + * @param {Transaction} _transaction */ - delete (transaction) { + delete (_transaction) { throw error.methodUnimplemented() } /** - * @param {StructStore} store + * @param {StructStore} _store */ - gc (store) { + gc (_store) { throw error.methodUnimplemented() } /** - * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder - * @param {number} offset + * @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder + * @param {number} _offset */ - write (encoder, offset) { + write (_encoder, _offset) { throw error.methodUnimplemented() } diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index c42fba718..746d0f010 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -10,7 +10,7 @@ import { getItemCleanStart, isDeleted, addToDeleteSet, - Transaction, Doc, Item, GC, DeleteSet, AbstractType, YEvent // eslint-disable-line + Transaction, Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line } from '../internals.js' import * as time from 'lib0/time' @@ -158,7 +158,7 @@ export class UndoManager extends Observable { */ constructor (typeScope, { captureTimeout = 500, - captureTransaction = tr => true, + captureTransaction = _tr => true, deleteFilter = () => true, trackedOrigins = new Set([null]), ignoreRemoteMapChanges = false, diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 1c6cecf97..1844af86b 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -1,4 +1,4 @@ -import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line +import { init } from './testHelper.js' // eslint-disable-line import * as Y from '../src/index.js' import * as t from 'lib0/testing' @@ -64,9 +64,9 @@ export const testUndoText = tc => { /** * Test case to fix #241 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testEmptyTypeScope = tc => { +export const testEmptyTypeScope = _tc => { const ydoc = new Y.Doc() const um = new Y.UndoManager([], { doc: ydoc }) const yarray = ydoc.getArray() @@ -78,9 +78,9 @@ export const testEmptyTypeScope = tc => { /** * Test case to fix #241 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testDoubleUndo = tc => { +export const testDoubleUndo = _tc => { const doc = new Y.Doc() const text = doc.getText() text.insert(0, '1221') @@ -316,9 +316,9 @@ export const testUndoDeleteFilter = tc => { /** * This issue has been reported in https://discuss.yjs.dev/t/undomanager-with-external-updates/454/6 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testUndoUntilChangePerformed = tc => { +export const testUndoUntilChangePerformed = _tc => { const doc = new Y.Doc() const doc2 = new Y.Doc() doc.on('update', update => Y.applyUpdate(doc2, update)) @@ -347,9 +347,9 @@ export const testUndoUntilChangePerformed = tc => { /** * This issue has been reported in https://github.com/yjs/yjs/issues/317 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testUndoNestedUndoIssue = tc => { +export const testUndoNestedUndoIssue = _tc => { const doc = new Y.Doc({ gc: false }) const design = doc.getMap() const undoManager = new Y.UndoManager(design, { captureTimeout: 0 }) @@ -403,9 +403,9 @@ export const testUndoNestedUndoIssue = tc => { /** * This issue has been reported in https://github.com/yjs/yjs/issues/355 * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testConsecutiveRedoBug = tc => { +export const testConsecutiveRedoBug = _tc => { const doc = new Y.Doc() const yRoot = doc.getMap() const undoMgr = new Y.UndoManager(yRoot) @@ -454,9 +454,9 @@ export const testConsecutiveRedoBug = tc => { /** * This issue has been reported in https://github.com/yjs/yjs/issues/304 * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testUndoXmlBug = tc => { +export const testUndoXmlBug = _tc => { const origin = 'origin' const doc = new Y.Doc() const fragment = doc.getXmlFragment('t') @@ -499,9 +499,9 @@ export const testUndoXmlBug = tc => { /** * This issue has been reported in https://github.com/yjs/yjs/issues/343 * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testUndoBlockBug = tc => { +export const testUndoBlockBug = _tc => { const doc = new Y.Doc({ gc: false }) const design = doc.getMap() @@ -559,9 +559,9 @@ export const testUndoBlockBug = tc => { * Undo text formatting delete should not corrupt peer state. * * @see https://github.com/yjs/yjs/issues/392 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testUndoDeleteTextFormat = tc => { +export const testUndoDeleteTextFormat = _tc => { const doc = new Y.Doc() const text = doc.getText() text.insert(0, 'Attack ships on fire off the shoulder of Orion.') @@ -597,9 +597,9 @@ export const testUndoDeleteTextFormat = tc => { * Undo text formatting delete should not corrupt peer state. * * @see https://github.com/yjs/yjs/issues/392 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testBehaviorOfIgnoreremotemapchangesProperty = tc => { +export const testBehaviorOfIgnoreremotemapchangesProperty = _tc => { const doc = new Y.Doc() const doc2 = new Y.Doc() doc.on('update', update => Y.applyUpdate(doc2, update, doc)) @@ -620,9 +620,9 @@ export const testBehaviorOfIgnoreremotemapchangesProperty = tc => { * Special deletion case. * * @see https://github.com/yjs/yjs/issues/447 - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSpecialDeletionCase = tc => { +export const testSpecialDeletionCase = _tc => { const origin = 'undoable' const doc = new Y.Doc() const fragment = doc.getXmlFragment() From 710ac31af3bc1520437fdc9b0e6eef9cc2993e89 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 3 Apr 2023 14:12:34 +0200 Subject: [PATCH 049/362] 13.5.52 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 069407a2c..b26efdb1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.51", + "version": "13.5.52", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.51", + "version": "13.5.52", "license": "MIT", "dependencies": { "lib0": "^0.2.72" diff --git a/package.json b/package.json index 92fafeaac..7df706cea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.51", + "version": "13.5.52", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From d039d48b3f75987211654065a45476a82feef4ce Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Apr 2023 20:07:17 +0200 Subject: [PATCH 050/362] ytext: diff should never create useless delta op --- src/types/YText.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 6f05b6d0d..8f4f0b603 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -631,36 +631,39 @@ export class YTextEvent extends YEvent { /** * @type {any} */ - let op + let op = null switch (action) { case 'delete': - op = { delete: deleteLen } + if (deleteLen > 0) { + op = { delete: deleteLen } + } deleteLen = 0 break case 'insert': - op = { insert } - if (currentAttributes.size > 0) { - op.attributes = {} - currentAttributes.forEach((value, key) => { - if (value !== null) { - op.attributes[key] = value - } - }) + if (typeof insert === 'object' || insert.length > 0) { + op = { insert } + if (currentAttributes.size > 0) { + op.attributes = {} + currentAttributes.forEach((value, key) => { + if (value !== null) { + op.attributes[key] = value + } + }) + } } insert = '' break case 'retain': - op = { retain } - if (Object.keys(attributes).length > 0) { - op.attributes = {} - for (const key in attributes) { - op.attributes[key] = attributes[key] + if (retain > 0) { + op = { retain } + if (!object.isEmpty(attributes)) { + op.attributes = object.assign({}, attributes) } } retain = 0 break } - delta.push(op) + if (op) delta.push(op) action = null } } From 5a8519d2c2488bd673012ceaa1c81ef0b0b29cf6 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Apr 2023 20:09:59 +0200 Subject: [PATCH 051/362] 13.5.53 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b26efdb1d..38c077dfb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.52", + "version": "13.5.53", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.52", + "version": "13.5.53", "license": "MIT", "dependencies": { "lib0": "^0.2.72" diff --git a/package.json b/package.json index 7df706cea..0f8626770 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.52", + "version": "13.5.53", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 39167e6e2aae3987c0a7390818346071694875c9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 22 Apr 2023 18:38:12 +0200 Subject: [PATCH 052/362] Implement function that obfuscates a ydoc and scrambles its content --- README.md | 24 ++++++ package-lock.json | 8 +- package.json | 2 +- src/index.js | 2 + src/utils/updates.js | 166 ++++++++++++++++++++++++++++++++++++++--- tests/updates.tests.js | 53 ++++++++++++- 6 files changed, 237 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 916c0c513..08b331fc6 100644 --- a/README.md +++ b/README.md @@ -753,6 +753,30 @@ currentState1 = Y.mergeUpdates([currentState1, diff2]) currentState1 = Y.mergeUpdates([currentState1, diff1]) ``` +#### Obfuscating Updates + +If one of your users runs into a weird bug (e.g. the rich-text editor throws +error messages), then you don't have to request the full document from your +user. Instead, they can obfuscate the document (i.e. replace the content with +meaningless generated content) before sending it to you. Note that someone might +still deduce the type of content by looking at the general structure of the +document. But this is much better than requesting the original document. + +Obfuscated updates contain all the CRDT-related data that is required for +merging. So it is safe to merge obfuscated updates. + +```javascript +const ydoc = new Y.Doc() +// perform some changes.. +ydoc.getText().insert(0, 'hello world') +const update = Y.encodeStateAsUpdate(ydoc) +// the below update contains scrambled data +const obfuscatedUpdate = Y.obfuscateUpdate(update) +const ydoc2 = new Y.Doc() +Y.applyUpdate(ydoc2, obfuscatedUpdate) +ydoc2.getText().toString() // => "00000000000" +``` + #### Using V2 update format Yjs implements two update formats. By default you are using the V1 update format. diff --git a/package-lock.json b/package-lock.json index 38c077dfb..7539f5c41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.5.53", "license": "MIT", "dependencies": { - "lib0": "^0.2.72" + "lib0": "^0.2.74" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", @@ -2481,9 +2481,9 @@ } }, "node_modules/lib0": { - "version": "0.2.73", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.73.tgz", - "integrity": "sha512-aJJIElCLWnHMcYZPtsM07QoSfHwpxCy4VUzBYGXFYEmh/h2QS5uZNbCCfL0CqnkOE30b7Tp9DVfjXag+3qzZjQ==", + "version": "0.2.74", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.74.tgz", + "integrity": "sha512-roj9i46/JwG5ik5KNTkxP2IytlnrssAkD/OhlAVtE+GqectrdkfR+pttszVLrOzMDeXNs1MPt6yo66MUolWSiA==", "dependencies": { "isomorphic.js": "^0.2.4" }, diff --git a/package.json b/package.json index 0f8626770..27588fa4d 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.72" + "lib0": "^0.2.74" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", diff --git a/src/index.js b/src/index.js index 6624f8cb8..d23c37f5f 100644 --- a/src/index.js +++ b/src/index.js @@ -90,6 +90,8 @@ export { diffUpdateV2, convertUpdateFormatV1ToV2, convertUpdateFormatV2ToV1, + obfuscateUpdate, + obfuscateUpdateV2, UpdateEncoderV1 } from './internals.js' diff --git a/src/utils/updates.js b/src/utils/updates.js index 950f1413b..c64ce355e 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -2,19 +2,40 @@ import * as binary from 'lib0/binary' import * as decoding from 'lib0/decoding' import * as encoding from 'lib0/encoding' +import * as error from 'lib0/error' +import * as f from 'lib0/function' import * as logging from 'lib0/logging' +import * as map from 'lib0/map' import * as math from 'lib0/math' +import * as string from 'lib0/string' + import { + ContentAny, + ContentBinary, + ContentDeleted, + ContentDoc, + ContentEmbed, + ContentFormat, + ContentJSON, + ContentString, + ContentType, createID, - readItemContent, - readDeleteSet, - writeDeleteSet, - Skip, - mergeDeleteSets, + decodeStateVector, DSEncoderV1, DSEncoderV2, - decodeStateVector, - Item, GC, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2 // eslint-disable-line + GC, + Item, + mergeDeleteSets, + readDeleteSet, + readItemContent, + Skip, + UpdateDecoderV1, + UpdateDecoderV2, + UpdateEncoderV1, + UpdateEncoderV2, + writeDeleteSet, + YXmlElement, + YXmlHook } from '../internals.js' /** @@ -552,17 +573,17 @@ const finishLazyStructWriting = (lazyWriter) => { /** * @param {Uint8Array} update + * @param {function(Item|GC|Skip):Item|GC|Skip} blockTransformer * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} YDecoder * @param {typeof UpdateEncoderV2 | typeof UpdateEncoderV1 } YEncoder */ -export const convertUpdateFormat = (update, YDecoder, YEncoder) => { +export const convertUpdateFormat = (update, blockTransformer, YDecoder, YEncoder) => { const updateDecoder = new YDecoder(decoding.createDecoder(update)) const lazyDecoder = new LazyStructReader(updateDecoder, false) const updateEncoder = new YEncoder() const lazyWriter = new LazyStructWriter(updateEncoder) - for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) { - writeStructToLazyStructWriter(lazyWriter, curr, 0) + writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0) } finishLazyStructWriting(lazyWriter) const ds = readDeleteSet(updateDecoder) @@ -570,12 +591,133 @@ export const convertUpdateFormat = (update, YDecoder, YEncoder) => { return updateEncoder.toUint8Array() } +/** + * @typedef {Object} ObfuscatorOptions + * @property {boolean} [ObfuscatorOptions.formatting=true] + * @property {boolean} [ObfuscatorOptions.subdocs=true] + * @property {boolean} [ObfuscatorOptions.yxml=true] Whether to obfuscate nodeName / hookName + */ + +/** + * @param {ObfuscatorOptions} obfuscator + */ +const createObfuscator = ({ formatting = true, subdocs = true, yxml = true } = {}) => { + let i = 0 + const mapKeyCache = map.create() + const nodeNameCache = map.create() + const formattingKeyCache = map.create() + const formattingValueCache = map.create() + formattingValueCache.set(null, null) // end of a formatting range should always be the end of a formatting range + /** + * @param {Item|GC|Skip} block + * @return {Item|GC|Skip} + */ + return block => { + switch (block.constructor) { + case GC: + case Skip: + return block + case Item: { + const item = /** @type {Item} */ (block) + const content = item.content + switch (content.constructor) { + case ContentDeleted: + break + case ContentType: { + if (yxml) { + const type = /** @type {ContentType} */ (content).type + if (type instanceof YXmlElement) { + type.nodeName = map.setIfUndefined(nodeNameCache, type.nodeName, () => 'node-' + i) + } + if (type instanceof YXmlHook) { + type.hookName = map.setIfUndefined(nodeNameCache, type.hookName, () => 'hook-' + i) + } + } + break + } + case ContentAny: { + const c = /** @type {ContentAny} */ (content) + c.arr = c.arr.map(() => i) + break + } + case ContentBinary: { + const c = /** @type {ContentBinary} */ (content) + c.content = new Uint8Array([i]) + break + } + case ContentDoc: { + const c = /** @type {ContentDoc} */ (content) + if (subdocs) { + c.opts = {} + c.doc.guid = i + '' + } + break + } + case ContentEmbed: { + const c = /** @type {ContentEmbed} */ (content) + c.embed = {} + break + } + case ContentFormat: { + const c = /** @type {ContentFormat} */ (content) + if (formatting) { + c.key = map.setIfUndefined(formattingKeyCache, c.key, () => i + '') + c.value = map.setIfUndefined(formattingValueCache, c.value, () => ({ i })) + } + break + } + case ContentJSON: { + const c = /** @type {ContentJSON} */ (content) + c.arr = c.arr.map(() => i) + break + } + case ContentString: { + const c = /** @type {ContentString} */ (content) + c.str = string.repeat((i % 10) + '', c.str.length) + break + } + default: + // unknown content type + error.unexpectedCase() + } + if (item.parentSub) { + item.parentSub = map.setIfUndefined(mapKeyCache, item.parentSub, () => i + '') + } + i++ + return block + } + default: + // unknown block-type + error.unexpectedCase() + } + } +} + +/** + * This function obfuscates the content of a Yjs update. This is useful to share + * buggy Yjs documents while significantly limiting the possibility that a + * developer can on the user. Note that it might still be possible to deduce + * some information by analyzing the "structure" of the document or by analyzing + * the typing behavior using the CRDT-related metadata that is still kept fully + * intact. + * + * @param {Uint8Array} update + * @param {ObfuscatorOptions} [opts] + */ +export const obfuscateUpdate = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV1, UpdateEncoderV1) + +/** + * @param {Uint8Array} update + * @param {ObfuscatorOptions} [opts] + */ +export const obfuscateUpdateV2 = (update, opts) => convertUpdateFormat(update, createObfuscator(opts), UpdateDecoderV2, UpdateEncoderV2) + /** * @param {Uint8Array} update */ -export const convertUpdateFormatV1ToV2 = update => convertUpdateFormat(update, UpdateDecoderV1, UpdateEncoderV2) +export const convertUpdateFormatV1ToV2 = update => convertUpdateFormat(update, f.id, UpdateDecoderV1, UpdateEncoderV2) /** * @param {Uint8Array} update */ -export const convertUpdateFormatV2ToV1 = update => convertUpdateFormat(update, UpdateDecoderV2, UpdateEncoderV1) +export const convertUpdateFormatV2ToV1 = update => convertUpdateFormat(update, f.id, UpdateDecoderV2, UpdateEncoderV1) diff --git a/tests/updates.tests.js b/tests/updates.tests.js index dce09e71b..4ba3bab62 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -4,6 +4,7 @@ import * as Y from '../src/index.js' import { readClientsStructRefs, readDeleteSet, UpdateDecoderV2, UpdateEncoderV2, writeDeleteSet } from '../src/internals.js' import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' +import * as object from 'lib0/object' /** * @typedef {Object} Enc @@ -138,7 +139,6 @@ export const testKeyEncoding = tc => { */ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { const cases = [] - // Case 1: Simple case, simply merge everything cases.push(enc.mergeUpdates(updates)) @@ -304,3 +304,54 @@ export const testMergePendingUpdates = tc => { const yText5 = yDoc5.getText('textBlock') t.compareStrings(yText5.toString(), 'nenor') } + +/** + * @param {t.TestCase} tc + */ +export const testObfuscateUpdates = tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText('text') + const ymap = ydoc.getMap('map') + const yarray = ydoc.getArray('array') + // test ytext + ytext.applyDelta([{ insert: 'text', attributes: { bold: true } }, { insert: { href: 'supersecreturl' } }]) + // test ymap + ymap.set('key', 'secret1') + ymap.set('key', 'secret2') + // test yarray with subtype & subdoc + const subtype = new Y.XmlElement('secretnodename') + const subdoc = new Y.Doc({ guid: 'secret' }) + subtype.setAttribute('attr', 'val') + yarray.insert(0, ['teststring', 42, subtype, subdoc]) + // obfuscate the content and put it into a new document + const obfuscatedUpdate = Y.obfuscateUpdate(Y.encodeStateAsUpdate(ydoc)) + const odoc = new Y.Doc() + Y.applyUpdate(odoc, obfuscatedUpdate) + const otext = odoc.getText('text') + const omap = odoc.getMap('map') + const oarray = odoc.getArray('array') + // test ytext + const delta = otext.toDelta() + t.assert(delta.length === 2) + t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4) + t.assert(object.length(delta[0].attributes) === 1) + t.assert(!object.hasProperty(delta[0].attributes, 'bold')) + t.assert(object.length(delta[1]) === 1) + t.assert(object.hasProperty(delta[1], 'insert')) + // test ymap + t.assert(omap.size === 1) + t.assert(!omap.has('key')) + // test yarray with subtype & subdoc + const result = oarray.toArray() + t.assert(result.length === 4) + t.assert(result[0] !== 'teststring') + t.assert(result[1] !== 42) + const osubtype = /** @type {Y.XmlElement} */ (result[2]) + const osubdoc = result[3] + // test subtype + t.assert(osubtype.nodeName !== subtype.nodeName) + t.assert(object.length(osubtype.getAttributes()) === 1) + t.assert(osubtype.getAttribute('attr') === undefined) + // test subdoc + t.assert(osubdoc.guid !== subdoc.guid) +} From 1f2f08ef7e953b9ff7d8eeebfece69d52465d048 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 22 Apr 2023 18:41:44 +0200 Subject: [PATCH 053/362] 13.6.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7539f5c41..950f234ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.5.53", + "version": "13.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.5.53", + "version": "13.6.0", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index 27588fa4d..7712c6079 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.5.53", + "version": "13.6.0", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From adaa95ebb84860b809ece199197349b69b7ecd43 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 27 Apr 2023 18:08:10 +0200 Subject: [PATCH 054/362] add example to createDocFromSnapshot - #159 --- src/utils/Snapshot.js | 10 +++++++++- tests/snapshot.tests.js | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index c9aa19e9b..a13bb5596 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -153,6 +153,14 @@ export const splitSnapshotAffectedStructs = (transaction, snapshot) => { } /** + * @example + * const ydoc = new Y.Doc({ gc: false }) + * ydoc.getText().insert(0, 'world!') + * const snapshot = Y.snapshot(ydoc) + * ydoc.getText().insert(0, 'hello ') + * const restored = Y.createDocFromSnapshot(ydoc, snapshot) + * assert(restored.getText().toString() === 'world!') + * * @param {Doc} originDoc * @param {Snapshot} snapshot * @param {Doc} [newDoc] Optionally, you may define the Yjs document that receives the data from originDoc @@ -161,7 +169,7 @@ export const splitSnapshotAffectedStructs = (transaction, snapshot) => { export const createDocFromSnapshot = (originDoc, snapshot, newDoc = new Doc()) => { if (originDoc.gc) { // we should not try to restore a GC-ed document, because some of the restored items might have their content deleted - throw new Error('originDoc must not be garbage collected') + throw new Error('Garbage-collection must be disabled in `originDoc`!') } const { sv, ds } = snapshot diff --git a/tests/snapshot.tests.js b/tests/snapshot.tests.js index 7f26718df..cd3e4773b 100644 --- a/tests/snapshot.tests.js +++ b/tests/snapshot.tests.js @@ -2,6 +2,18 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' import { init } from './testHelper.js' +/** + * @param {t.TestCase} tc + */ +export const testBasic = tc => { + const ydoc = new Y.Doc({ gc: false }) + ydoc.getText().insert(0, 'world!') + const snapshot = Y.snapshot(ydoc) + ydoc.getText().insert(0, 'hello ') + const restored = Y.createDocFromSnapshot(ydoc, snapshot) + t.assert(restored.getText().toString() === 'world!') +} + /** * @param {t.TestCase} tc */ From 61eeaef22612162cdc7e942b51130fe338ec1715 Mon Sep 17 00:00:00 2001 From: James Pearce Date: Sun, 30 Apr 2023 16:31:46 -0400 Subject: [PATCH 055/362] Add missing getting-started steps --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 08b331fc6..8e5fff2b0 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,9 @@ PORT=1234 node ./node_modules/y-websocket/bin/server.js ### Example: Observe types ```js +import * as Y from 'yjs'; + +const doc = new Y.Doc(); const yarray = doc.getArray('my-array') yarray.observe(event => { console.log('yarray was modified') From 30b56d5ae98034a36f950d18aba8bbf989557296 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 4 May 2023 10:07:05 +0200 Subject: [PATCH 056/362] Enable typings for inserting custom attrs in YXmlElement - fixes #531 --- src/types/YXmlElement.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index ce8335ce3..905ad2a3f 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -18,6 +18,8 @@ import { * * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement + * + * @template {{ [key: string]: Object|number|null|Array|string|Uint8Array|AbstractType }} [KV={ [key: string]: string }] */ export class YXmlElement extends YXmlFragment { constructor (nodeName = 'UNDEFINED') { @@ -116,7 +118,7 @@ export class YXmlElement extends YXmlFragment { /** * Removes an attribute from this YXmlElement. * - * @param {String} attributeName The attribute name that is to be removed. + * @param {string} attributeName The attribute name that is to be removed. * * @public */ @@ -133,8 +135,10 @@ export class YXmlElement extends YXmlFragment { /** * Sets or updates an attribute. * - * @param {String} attributeName The attribute name that is to be set. - * @param {String} attributeValue The attribute value that is to be set. + * @template {keyof KV & string} KEY + * + * @param {KEY} attributeName The attribute name that is to be set. + * @param {KV[KEY]} attributeValue The attribute value that is to be set. * * @public */ @@ -151,9 +155,11 @@ export class YXmlElement extends YXmlFragment { /** * Returns an attribute value that belongs to the attribute name. * - * @param {String} attributeName The attribute name that identifies the + * @template {keyof KV & string} KEY + * + * @param {KEY} attributeName The attribute name that identifies the * queried value. - * @return {String} The queried attribute value. + * @return {KV[KEY]|undefined} The queried attribute value. * * @public */ @@ -164,7 +170,7 @@ export class YXmlElement extends YXmlFragment { /** * Returns whether an attribute exists * - * @param {String} attributeName The attribute name to check for existence. + * @param {string} attributeName The attribute name to check for existence. * @return {boolean} whether the attribute exists. * * @public From 83712cb1a67124f9f91467ae4f68dc9f841ce188 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 4 May 2023 11:26:11 +0200 Subject: [PATCH 057/362] update typings of getAttributes --- src/types/YMap.js | 4 +++- src/types/YXmlElement.js | 29 ++++++++++++++++++------- tests/y-xml.tests.js | 47 +++++++++++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/types/YMap.js b/src/types/YMap.js index c640ae543..e2dd7a496 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -206,9 +206,11 @@ export class YMap extends AbstractType { /** * Adds or updates an element with a specified key and value. + * @template {MapType} VAL * * @param {string} key The key of the element to add to this YMap - * @param {MapType} value The value of the element to add + * @param {VAL} value The value of the element to add + * @return {VAL} */ set (key, value) { if (this.doc !== null) { diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 905ad2a3f..92088cdd1 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -1,3 +1,4 @@ +import * as object from 'lib0/object' import { YXmlFragment, @@ -12,6 +13,10 @@ import { YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line } from '../internals.js' +/** + * @typedef {Object|number|null|Array|string|Uint8Array|AbstractType} ValueTypes + */ + /** * An YXmlElement imitates the behavior of a * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}. @@ -19,7 +24,7 @@ import { * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement * - * @template {{ [key: string]: Object|number|null|Array|string|Uint8Array|AbstractType }} [KV={ [key: string]: string }] + * @template {{ [key: string]: ValueTypes }} [KV={ [key: string]: string }] */ export class YXmlElement extends YXmlFragment { constructor (nodeName = 'UNDEFINED') { @@ -75,14 +80,19 @@ export class YXmlElement extends YXmlFragment { } /** - * @return {YXmlElement} + * @return {YXmlElement} */ clone () { + /** + * @type {YXmlElement} + */ const el = new YXmlElement(this.nodeName) const attrs = this.getAttributes() - for (const key in attrs) { - el.setAttribute(key, attrs[key]) - } + object.forEach(attrs, (value, key) => { + if (typeof value === 'string') { + el.setAttribute(key, value) + } + }) // @ts-ignore el.insert(0, this.toArray().map(item => item instanceof AbstractType ? item.clone() : item)) return el @@ -182,12 +192,12 @@ export class YXmlElement extends YXmlFragment { /** * Returns all attribute name/value pairs in a JSON Object. * - * @return {Object} A JSON Object that describes the attributes. + * @return {{ [Key in Extract]?: KV[Key]}} A JSON Object that describes the attributes. * * @public */ getAttributes () { - return typeMapGetAll(this) + return /** @type {any} */ (typeMapGetAll(this)) } /** @@ -209,7 +219,10 @@ export class YXmlElement extends YXmlFragment { const dom = _document.createElement(this.nodeName) const attrs = this.getAttributes() for (const key in attrs) { - dom.setAttribute(key, attrs[key]) + const value = attrs[key] + if (typeof value === 'string') { + dom.setAttribute(key, value) + } } typeListForEach(this, yxml => { dom.appendChild(yxml.toDOM(_document, hooks, binding)) diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 619d9ee91..1ae2c8139 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -3,6 +3,33 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' +export const testCustomTypings = () => { + const ydoc = new Y.Doc() + const ymap = ydoc.getMap() + /** + * @type {Y.XmlElement<{ num: number, str: string, [k:string]: object|number|string }>} + */ + const yxml = ymap.set('yxml', new Y.XmlElement('test')) + /** + * @type {number|undefined} + */ + const num = yxml.getAttribute('num') + /** + * @type {string|undefined} + */ + const str = yxml.getAttribute('str') + /** + * @type {object|number|string|undefined} + */ + const dtrn = yxml.getAttribute('dtrn') + const attrs = yxml.getAttributes() + /** + * @type {object|number|string|undefined} + */ + const any = attrs.shouldBeAny + console.log({ num, str, dtrn, attrs, any }) +} + /** * @param {t.TestCase} tc */ @@ -92,9 +119,9 @@ export const testTreewalker = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testYtextAttributes = tc => { +export const testYtextAttributes = _tc => { const ydoc = new Y.Doc() const ytext = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) ytext.observe(event => { @@ -106,9 +133,9 @@ export const testYtextAttributes = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSiblings = tc => { +export const testSiblings = _tc => { const ydoc = new Y.Doc() const yxml = ydoc.getXmlFragment() const first = new Y.XmlText() @@ -122,9 +149,9 @@ export const testSiblings = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testInsertafter = tc => { +export const testInsertafter = _tc => { const ydoc = new Y.Doc() const yxml = ydoc.getXmlFragment() const first = new Y.XmlText() @@ -152,9 +179,9 @@ export const testInsertafter = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testClone = tc => { +export const testClone = _tc => { const ydoc = new Y.Doc() const yxml = ydoc.getXmlFragment() const first = new Y.XmlText('text') @@ -170,9 +197,9 @@ export const testClone = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testFormattingBug = tc => { +export const testFormattingBug = _tc => { const ydoc = new Y.Doc() const yxml = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) const delta = [ From 2e9a648d081990fcd74f18f662a2ce416d078a76 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 4 May 2023 11:29:08 +0200 Subject: [PATCH 058/362] 13.6.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 950f234ac..f479a8d10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.0", + "version": "13.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.0", + "version": "13.6.1", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index 7712c6079..3919dcda6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.0", + "version": "13.6.1", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 1ce17514321415d526130df977277630ac5cba44 Mon Sep 17 00:00:00 2001 From: Liucw Date: Wed, 17 May 2023 14:52:25 +0800 Subject: [PATCH 059/362] docs: fix "Example: Syncing clients without loading the Y.Doc" code --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e5fff2b0..8b755dfd3 100644 --- a/README.md +++ b/README.md @@ -753,7 +753,7 @@ const diff2 = Y.diffUpdate(currentState2, stateVector1) // sync clients currentState1 = Y.mergeUpdates([currentState1, diff2]) -currentState1 = Y.mergeUpdates([currentState1, diff1]) +currentState2 = Y.mergeUpdates([currentState2, diff1]) ``` #### Obfuscating Updates From 719858201af1066d3d9759818bf4fb328c9ddcda Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 8 Jun 2023 11:14:49 +0200 Subject: [PATCH 060/362] implement snapshotContainsUpdate --- src/index.js | 4 ++- src/utils/DeleteSet.js | 20 +++++++++++++++ src/utils/Snapshot.js | 32 +++++++++++++++++++++-- src/utils/encoding.js | 5 ++-- tests/snapshot.tests.js | 57 +++++++++++++++++++++++++++++------------ tests/testHelper.js | 22 ++-------------- 6 files changed, 98 insertions(+), 42 deletions(-) diff --git a/src/index.js b/src/index.js index d23c37f5f..781e8eb48 100644 --- a/src/index.js +++ b/src/index.js @@ -92,7 +92,9 @@ export { convertUpdateFormatV2ToV1, obfuscateUpdate, obfuscateUpdateV2, - UpdateEncoderV1 + UpdateEncoderV1, + equalDeleteSets, + snapshotContainsUpdate } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 429c3b913..e5e964dde 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -328,3 +328,23 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => { } return null } + +/** + * @param {DeleteSet} ds1 + * @param {DeleteSet} ds2 + */ +export const equalDeleteSets = (ds1, ds2) => { + if (ds1.clients.size !== ds2.clients.size) return false + ds1.clients.forEach((deleteItems1, client) => { + const deleteItems2 = /** @type {Array} */ (ds2.clients.get(client)) + if (deleteItems2 === undefined || deleteItems1.length !== deleteItems2.length) return false + for (let i = 0; i < deleteItems1.length; i++) { + const di1 = deleteItems1[i] + const di2 = deleteItems2[i] + if (di1.clock !== di2.clock || di1.len !== di2.len) { + return false + } + } + }) + return true +} diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index a13bb5596..dfd82c864 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -15,7 +15,10 @@ import { findIndexSS, UpdateEncoderV2, applyUpdateV2, - DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item // eslint-disable-line + LazyStructReader, + equalDeleteSets, + UpdateDecoderV1, UpdateDecoderV2, DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item, // eslint-disable-line + mergeDeleteSets } from '../internals.js' import * as map from 'lib0/map' @@ -147,7 +150,7 @@ export const splitSnapshotAffectedStructs = (transaction, snapshot) => { getItemCleanStart(transaction, createID(client, clock)) } }) - iterateDeletedStructs(transaction, snapshot.ds, item => {}) + iterateDeletedStructs(transaction, snapshot.ds, _item => {}) meta.add(snapshot) } } @@ -207,3 +210,28 @@ export const createDocFromSnapshot = (originDoc, snapshot, newDoc = new Doc()) = applyUpdateV2(newDoc, encoder.toUint8Array(), 'snapshot') return newDoc } + +/** + * @param {Snapshot} snapshot + * @param {Uint8Array} update + * @param {typeof UpdateDecoderV2 | typeof UpdateDecoderV1} [YDecoder] + */ +export const snapshotContainsUpdateV2 = (snapshot, update, YDecoder = UpdateDecoderV2) => { + const structs = [] + const updateDecoder = new YDecoder(decoding.createDecoder(update)) + const lazyDecoder = new LazyStructReader(updateDecoder, false) + for (let curr = lazyDecoder.curr; curr !== null; curr = lazyDecoder.next()) { + structs.push(curr) + if ((snapshot.sv.get(curr.id.client) || 0) < curr.id.clock + curr.length) { + return false + } + } + const mergedDS = mergeDeleteSets([snapshot.ds, readDeleteSet(updateDecoder)]) + return equalDeleteSets(snapshot.ds, mergedDS) +} + +/** + * @param {Snapshot} snapshot + * @param {Uint8Array} update + */ +export const snapshotContainsUpdate = (snapshot, update) => snapshotContainsUpdateV2(snapshot, update, UpdateDecoderV1) diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 56e5dac17..83e1b91fa 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -88,7 +88,7 @@ export const writeClientsStructs = (encoder, store, _sm) => { sm.set(client, clock) } }) - getStateVector(store).forEach((clock, client) => { + getStateVector(store).forEach((_clock, client) => { if (!_sm.has(client)) { sm.set(client, 0) } @@ -98,8 +98,7 @@ export const writeClientsStructs = (encoder, store, _sm) => { // Write items with higher client ids first // This heavily improves the conflict algorithm. array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => { - // @ts-ignore - writeStructs(encoder, store.clients.get(client), client, clock) + writeStructs(encoder, /** @type {Array} */ (store.clients.get(client)), client, clock) }) } diff --git a/tests/snapshot.tests.js b/tests/snapshot.tests.js index cd3e4773b..d72e7ba74 100644 --- a/tests/snapshot.tests.js +++ b/tests/snapshot.tests.js @@ -3,9 +3,9 @@ import * as t from 'lib0/testing' import { init } from './testHelper.js' /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testBasic = tc => { +export const testBasic = _tc => { const ydoc = new Y.Doc({ gc: false }) ydoc.getText().insert(0, 'world!') const snapshot = Y.snapshot(ydoc) @@ -15,9 +15,9 @@ export const testBasic = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testBasicRestoreSnapshot = tc => { +export const testBasicRestoreSnapshot = _tc => { const doc = new Y.Doc({ gc: false }) doc.getArray('array').insert(0, ['hello']) const snap = Y.snapshot(doc) @@ -30,9 +30,9 @@ export const testBasicRestoreSnapshot = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testEmptyRestoreSnapshot = tc => { +export const testEmptyRestoreSnapshot = _tc => { const doc = new Y.Doc({ gc: false }) const snap = Y.snapshot(doc) snap.sv.set(9999, 0) @@ -50,9 +50,9 @@ export const testEmptyRestoreSnapshot = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testRestoreSnapshotWithSubType = tc => { +export const testRestoreSnapshotWithSubType = _tc => { const doc = new Y.Doc({ gc: false }) doc.getArray('array').insert(0, [new Y.Map()]) const subMap = doc.getArray('array').get(0) @@ -73,9 +73,9 @@ export const testRestoreSnapshotWithSubType = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testRestoreDeletedItem1 = tc => { +export const testRestoreDeletedItem1 = _tc => { const doc = new Y.Doc({ gc: false }) doc.getArray('array').insert(0, ['item1', 'item2']) @@ -89,9 +89,9 @@ export const testRestoreDeletedItem1 = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testRestoreLeftItem = tc => { +export const testRestoreLeftItem = _tc => { const doc = new Y.Doc({ gc: false }) doc.getArray('array').insert(0, ['item1']) doc.getMap('map').set('test', 1) @@ -107,9 +107,9 @@ export const testRestoreLeftItem = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testDeletedItemsBase = tc => { +export const testDeletedItemsBase = _tc => { const doc = new Y.Doc({ gc: false }) doc.getArray('array').insert(0, ['item1']) doc.getArray('array').delete(0) @@ -123,9 +123,9 @@ export const testDeletedItemsBase = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testDeletedItems2 = tc => { +export const testDeletedItems2 = _tc => { const doc = new Y.Doc({ gc: false }) doc.getArray('array').insert(0, ['item1', 'item2', 'item3']) doc.getArray('array').delete(1) @@ -181,3 +181,28 @@ export const testDependentChanges = tc => { const docRestored1 = Y.createDocFromSnapshot(array1.doc, snap) t.compare(docRestored1.getArray('array').toArray(), ['user1item1', 'user2item1']) } + +/** + * @param {t.TestCase} _tc + */ +export const testContainsUpdate = _tc => { + const ydoc = new Y.Doc() + /** + * @type {Array} + */ + const updates = [] + ydoc.on('update', update => { + updates.push(update) + }) + const yarr = ydoc.getArray() + const snapshot1 = Y.snapshot(ydoc) + yarr.insert(0, [1]) + const snapshot2 = Y.snapshot(ydoc) + yarr.delete(0, 1) + const snapshotFinal = Y.snapshot(ydoc) + t.assert(!Y.snapshotContainsUpdate(snapshot1, updates[0])) + t.assert(!Y.snapshotContainsUpdate(snapshot2, updates[1])) + t.assert(Y.snapshotContainsUpdate(snapshot2, updates[0])) + t.assert(Y.snapshotContainsUpdate(snapshotFinal, updates[0])) + t.assert(Y.snapshotContainsUpdate(snapshotFinal, updates[1])) +} diff --git a/tests/testHelper.js b/tests/testHelper.js index 8c90a099c..f1ff47561 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -356,8 +356,9 @@ export const compare = users => { return true }) t.compare(Y.encodeStateVector(users[i]), Y.encodeStateVector(users[i + 1])) - compareDS(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store)) + Y.equalDeleteSets(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store)) compareStructStores(users[i].store, users[i + 1].store) + t.compare(Y.encodeSnapshot(Y.snapshot(users[i])), Y.encodeSnapshot(Y.snapshot(users[i + 1]))) } users.map(u => u.destroy()) } @@ -412,25 +413,6 @@ export const compareStructStores = (ss1, ss2) => { } } -/** - * @param {import('../src/internals.js').DeleteSet} ds1 - * @param {import('../src/internals.js').DeleteSet} ds2 - */ -export const compareDS = (ds1, ds2) => { - t.assert(ds1.clients.size === ds2.clients.size) - ds1.clients.forEach((deleteItems1, client) => { - const deleteItems2 = /** @type {Array} */ (ds2.clients.get(client)) - t.assert(deleteItems2 !== undefined && deleteItems1.length === deleteItems2.length) - for (let i = 0; i < deleteItems1.length; i++) { - const di1 = deleteItems1[i] - const di2 = deleteItems2[i] - if (di1.clock !== di2.clock || di1.len !== di2.len) { - t.fail('DeleteSets dont match') - } - } - }) -} - /** * @template T * @callback InitTestObjectCallback From 00ef472d68545cb260abd35c2de4b3b78719c9e4 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 8 Jun 2023 11:19:06 +0200 Subject: [PATCH 061/362] 13.6.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f479a8d10..c3bc38b17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.1", + "version": "13.6.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.1", + "version": "13.6.2", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index 3919dcda6..b5ed95ac3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.1", + "version": "13.6.2", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 3741f43a116a81a288e4ad9f1231d4e129812076 Mon Sep 17 00:00:00 2001 From: Noel Levy Date: Mon, 12 Jun 2023 16:56:19 -0700 Subject: [PATCH 062/362] group cleanups for YText changes into a single transaction Fixes #522 but is still massively slow --- src/types/YText.js | 11 ++++++++++- src/utils/Transaction.js | 13 ++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 8f4f0b603..dad41bdca 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -859,12 +859,21 @@ export class YText extends AbstractType { _callObserver (transaction, parentSubs) { super._callObserver(transaction, parentSubs) const event = new YTextEvent(this, transaction, parentSubs) - const doc = transaction.doc callTypeObservers(this, transaction, event) // If a remote change happened, we try to cleanup potential formatting duplicates. + if (!transaction.local) { + transaction._yTexts.push(this) + } + } + + /** + * @param {Transaction} transaction + */ + _cleanup (transaction) { if (!transaction.local) { // check if another formatting item was inserted let foundFormattingItem = false + const doc = transaction.doc for (const [client, afterClock] of transaction.afterState.entries()) { const clock = transaction.beforeState.get(client) || 0 if (afterClock === clock) { diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 1e553a902..ea04d1d1a 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -11,7 +11,7 @@ import { Item, generateNewClientId, createID, - UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line + UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, YText // eslint-disable-line } from '../internals.js' import * as map from 'lib0/map' @@ -114,6 +114,10 @@ export class Transaction { * @type {Set} */ this.subdocsLoaded = new Set() + /** + * @type {Array} + */ + this._yTexts = [] } } @@ -295,6 +299,13 @@ const cleanupTransactions = (transactionCleanups, i) => { fs.push(() => doc.emit('afterTransaction', [transaction, doc])) }) callAll(fs, []) + if (transaction._yTexts.length > 0) { + transact(doc, () => { + transaction._yTexts.forEach(yText => { + yText._cleanup(transaction) + }) + }) + } } finally { // Replace deleted items with ItemDeleted / GC. // This is where content is actually remove from the Yjs Doc. From 08801dd406c5b9a0903f03513df92b152688db44 Mon Sep 17 00:00:00 2001 From: Noel Levy Date: Mon, 12 Jun 2023 18:20:06 -0700 Subject: [PATCH 063/362] scan the document once for all ytexts when cleaning up Fixes #522 but is a scarier change --- src/types/YText.js | 61 ++++++++++++++++++---------------------- src/utils/Transaction.js | 10 +++---- 2 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index dad41bdca..95df53913 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -862,47 +862,42 @@ export class YText extends AbstractType { callTypeObservers(this, transaction, event) // If a remote change happened, we try to cleanup potential formatting duplicates. if (!transaction.local) { - transaction._yTexts.push(this) + transaction._yTexts.add(this) } } /** * @param {Transaction} transaction */ - _cleanup (transaction) { - if (!transaction.local) { - // check if another formatting item was inserted - let foundFormattingItem = false - const doc = transaction.doc - for (const [client, afterClock] of transaction.afterState.entries()) { - const clock = transaction.beforeState.get(client) || 0 - if (afterClock === clock) { - continue - } - iterateStructs(transaction, /** @type {Array} */ (doc.store.clients.get(client)), clock, afterClock, item => { - if (!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat) { - foundFormattingItem = true - } - }) - if (foundFormattingItem) { - break + static _cleanup (transaction) { + const withFormattingItems = new Set() + // check if another formatting item was inserted + const doc = transaction.doc + for (const [client, afterClock] of transaction.afterState.entries()) { + const clock = transaction.beforeState.get(client) || 0 + if (afterClock === clock) { + continue + } + iterateStructs(transaction, /** @type {Array} */ (doc.store.clients.get(client)), clock, afterClock, item => { + if (!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && !(item instanceof GC) && transaction._yTexts.has(/** @type YText */ (item.parent))) { + withFormattingItems.add(item.parent) } + }) + } + iterateDeletedStructs(transaction, transaction.deleteSet, item => { + if (item instanceof GC) { + return } - if (!foundFormattingItem) { - iterateDeletedStructs(transaction, transaction.deleteSet, item => { - if (item instanceof GC || foundFormattingItem) { - return - } - if (item.parent === this && item.content.constructor === ContentFormat) { - foundFormattingItem = true - } - }) + if (transaction._yTexts.has(/** @type YText */ (item.parent)) && item.content.constructor === ContentFormat) { + withFormattingItems.add(item.parent) } - transact(doc, (t) => { - if (foundFormattingItem) { + }) + transact(doc, (t) => { + for (const yText of transaction._yTexts) { + if (withFormattingItems.has(yText)) { // If a formatting item was inserted, we simply clean the whole type. // We need to compute currentAttributes for the current position anyway. - cleanupYTextFormatting(this) + cleanupYTextFormatting(yText) } else { // If no formatting attribute was inserted, we can make due with contextless // formatting cleanups. @@ -911,13 +906,13 @@ export class YText extends AbstractType { if (item instanceof GC) { return } - if (item.parent === this) { + if (item.parent === yText) { cleanupContextlessFormattingGap(t, item) } }) } - }) - } + } + }) } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index ea04d1d1a..df1c39034 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -115,9 +115,9 @@ export class Transaction { */ this.subdocsLoaded = new Set() /** - * @type {Array} + * @type {Set} */ - this._yTexts = [] + this._yTexts = new Set() } } @@ -299,11 +299,9 @@ const cleanupTransactions = (transactionCleanups, i) => { fs.push(() => doc.emit('afterTransaction', [transaction, doc])) }) callAll(fs, []) - if (transaction._yTexts.length > 0) { + if (transaction._yTexts.size > 0) { transact(doc, () => { - transaction._yTexts.forEach(yText => { - yText._cleanup(transaction) - }) + YText._cleanup(transaction) }) } } finally { From ce098d0ac2b617d0f4c5214c95607dee8cfaba87 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 15 Jun 2023 12:40:28 +0200 Subject: [PATCH 064/362] refactor #538 (formatting attrs) a bit --- src/types/YText.js | 101 ++++++++++++++++++++------------------- src/utils/Transaction.js | 13 +++-- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 95df53913..79430c1a5 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -476,6 +476,56 @@ export const cleanupYTextFormatting = type => { return res } +/** + * This will be called by the transction once the event handlers are called to potentially cleanup + * formatting attributes. + * + * @param {Transaction} transaction + */ +export const cleanupYTextAfterTransaction = transaction => { + /** + * @type {Set} + */ + const needFullCleanup = new Set() + // check if another formatting item was inserted + const doc = transaction.doc + for (const [client, afterClock] of transaction.afterState.entries()) { + const clock = transaction.beforeState.get(client) || 0 + if (afterClock === clock) { + continue + } + iterateStructs(transaction, /** @type {Array} */ (doc.store.clients.get(client)), clock, afterClock, item => { + if ( + !item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC + ) { + needFullCleanup.add(/** @type {any} */ (item).parent) + } + }) + } + // cleanup in a new transaction + transact(doc, (t) => { + iterateDeletedStructs(transaction, transaction.deleteSet, item => { + if (item instanceof GC || needFullCleanup.has(/** @type {YText} */ (item.parent))) { + return + } + const parent = /** @type {YText} */ (item.parent) + if (item.content.constructor === ContentFormat) { + needFullCleanup.add(parent) + } else { + // If no formatting attribute was inserted or deleted, we can make due with contextless + // formatting cleanups. + // Contextless: it is not necessary to compute currentAttributes for the affected position. + cleanupContextlessFormattingGap(t, item) + } + }) + // If a formatting item was inserted, we simply clean the whole type. + // We need to compute currentAttributes for the current position anyway. + for (const yText of needFullCleanup) { + cleanupYTextFormatting(yText) + } + }) +} + /** * @param {Transaction} transaction * @param {ItemTextListPosition} currPos @@ -862,57 +912,8 @@ export class YText extends AbstractType { callTypeObservers(this, transaction, event) // If a remote change happened, we try to cleanup potential formatting duplicates. if (!transaction.local) { - transaction._yTexts.add(this) - } - } - - /** - * @param {Transaction} transaction - */ - static _cleanup (transaction) { - const withFormattingItems = new Set() - // check if another formatting item was inserted - const doc = transaction.doc - for (const [client, afterClock] of transaction.afterState.entries()) { - const clock = transaction.beforeState.get(client) || 0 - if (afterClock === clock) { - continue - } - iterateStructs(transaction, /** @type {Array} */ (doc.store.clients.get(client)), clock, afterClock, item => { - if (!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && !(item instanceof GC) && transaction._yTexts.has(/** @type YText */ (item.parent))) { - withFormattingItems.add(item.parent) - } - }) + transaction._needFormattingCleanup = true } - iterateDeletedStructs(transaction, transaction.deleteSet, item => { - if (item instanceof GC) { - return - } - if (transaction._yTexts.has(/** @type YText */ (item.parent)) && item.content.constructor === ContentFormat) { - withFormattingItems.add(item.parent) - } - }) - transact(doc, (t) => { - for (const yText of transaction._yTexts) { - if (withFormattingItems.has(yText)) { - // If a formatting item was inserted, we simply clean the whole type. - // We need to compute currentAttributes for the current position anyway. - cleanupYTextFormatting(yText) - } else { - // If no formatting attribute was inserted, we can make due with contextless - // formatting cleanups. - // Contextless: it is not necessary to compute currentAttributes for the affected position. - iterateDeletedStructs(t, t.deleteSet, item => { - if (item instanceof GC) { - return - } - if (item.parent === yText) { - cleanupContextlessFormattingGap(t, item) - } - }) - } - } - }) } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index df1c39034..299835ee5 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -11,7 +11,8 @@ import { Item, generateNewClientId, createID, - UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, YText // eslint-disable-line + cleanupYTextAfterTransaction, + UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line } from '../internals.js' import * as map from 'lib0/map' @@ -115,9 +116,9 @@ export class Transaction { */ this.subdocsLoaded = new Set() /** - * @type {Set} + * @type {boolean} */ - this._yTexts = new Set() + this._needFormattingCleanup = false } } @@ -299,10 +300,8 @@ const cleanupTransactions = (transactionCleanups, i) => { fs.push(() => doc.emit('afterTransaction', [transaction, doc])) }) callAll(fs, []) - if (transaction._yTexts.size > 0) { - transact(doc, () => { - YText._cleanup(transaction) - }) + if (transaction._needFormattingCleanup) { + cleanupYTextAfterTransaction(transaction) } } finally { // Replace deleted items with ItemDeleted / GC. From aedd4c8bf3a48c10df054830a8b55046febea436 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 15 Jun 2023 12:47:48 +0200 Subject: [PATCH 065/362] 13.6.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3bc38b17..d7c80ad77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.2", + "version": "13.6.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.2", + "version": "13.6.3", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index b5ed95ac3..781cb0759 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.2", + "version": "13.6.3", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 885a74047052ddce9f03dd8e0478de9b51b84bd4 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 15 Jun 2023 13:09:30 +0200 Subject: [PATCH 066/362] heavily improve performance when there are many events --- src/utils/YEvent.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index ac877bc51..8aafd94f6 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -44,6 +44,10 @@ export class YEvent { * @type {null | Array<{ insert?: string | Array | object | AbstractType, retain?: number, delete?: number, attributes?: Object }>} */ this._delta = null + /** + * @type {Array|null} + */ + this._path = null } /** @@ -60,8 +64,7 @@ export class YEvent { * type === event.target // => true */ get path () { - // @ts-ignore _item is defined because target is integrated - return getPathTo(this.currentTarget, this.target) + return this._path || (this._path = getPathTo(this.currentTarget, this.target)) } /** From 2fbba13246152584813164d9f0cce494dc8f54d2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 15 Jun 2023 13:11:40 +0200 Subject: [PATCH 067/362] 13.6.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7c80ad77..b8f0cae4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.3", + "version": "13.6.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.3", + "version": "13.6.4", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index 781cb0759..f2273225f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.3", + "version": "13.6.4", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From c398448152413b340af8be1d10c628a3d51c4f87 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 16 Jun 2023 16:04:18 +0200 Subject: [PATCH 068/362] add blocksuite editor by affine --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8e5fff2b0..e1ee09030 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ are implemented in separate modules. | [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](https://github.com/yjs/y-codemirror) | [demo](https://demos.yjs.dev/codemirror/codemirror.html) | | [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](https://github.com/yjs/y-monaco) | [demo](https://demos.yjs.dev/monaco/monaco.html) | | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | +| [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | | React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) | From 5d862477cd307b8c9e396e56524d76ef2b0698fa Mon Sep 17 00:00:00 2001 From: Noel Levy Date: Mon, 19 Jun 2023 11:31:45 -0700 Subject: [PATCH 069/362] invalidate cached path when changing currentTarget of event fixes #544 --- src/utils/Transaction.js | 1 + tests/y-map.tests.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 299835ee5..a7b23ff8e 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -287,6 +287,7 @@ const cleanupTransactions = (transactionCleanups, i) => { events .forEach(event => { event.currentTarget = type + event._path = null }) // sort events by path length so that top-level events are fired first. events diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index e12d55c9c..3356afc12 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -337,6 +337,34 @@ export const testObserversUsingObservedeep = tc => { compare(users) } +/** + * @param {t.TestCase} tc + */ +export const testPathsOfSiblingEvents = tc => { + const { users, map0 } = init(tc, { users: 2 }) + /** + * @type {Array>} + */ + const pathes = [] + let calls = 0 + const doc = users[0] + map0.set('map', new Y.Map()) + map0.get('map').set('text1', new Y.Text('initial')) + map0.observeDeep(events => { + events.forEach(event => { + pathes.push(event.path) + }) + calls++ + }) + doc.transact(() => { + map0.get('map').get('text1').insert(0, 'post-') + map0.get('map').set('text2', new Y.Text('new')) + }) + t.assert(calls === 1) + t.compare(pathes, [['map'], ['map', 'text1']]) + compare(users) +} + // TODO: Test events in Y.Map /** * @param {Object} is From 12be6c006aff0a5a4596fe5bd6d3347083f5b633 Mon Sep 17 00:00:00 2001 From: yousefed Date: Wed, 21 Jun 2023 18:28:53 +0200 Subject: [PATCH 070/362] fix equalDeleteSets --- src/utils/DeleteSet.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index e5e964dde..6d2a43d42 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -1,18 +1,26 @@ import { + DSDecoderV1, + DSDecoderV2, + DSEncoderV1, + DSEncoderV2, + GC, + ID // eslint-disable-line + , + Item, + StructStore, Transaction, + UpdateEncoderV2, findIndexSS, getState, - splitItem, iterateStructs, - UpdateEncoderV2, - DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line + splitItem } from '../internals.js' import * as array from 'lib0/array' -import * as math from 'lib0/math' -import * as map from 'lib0/map' -import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' +import * as encoding from 'lib0/encoding' +import * as map from 'lib0/map' +import * as math from 'lib0/math' export class DeleteItem { /** @@ -335,7 +343,7 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => { */ export const equalDeleteSets = (ds1, ds2) => { if (ds1.clients.size !== ds2.clients.size) return false - ds1.clients.forEach((deleteItems1, client) => { + for (const [client, deleteItems1] of ds1.clients.entries()) { const deleteItems2 = /** @type {Array} */ (ds2.clients.get(client)) if (deleteItems2 === undefined || deleteItems1.length !== deleteItems2.length) return false for (let i = 0; i < deleteItems1.length; i++) { @@ -345,6 +353,6 @@ export const equalDeleteSets = (ds1, ds2) => { return false } } - }) + } return true } From eda085936a543e54f87c0fac9dcfcc455117ba4b Mon Sep 17 00:00:00 2001 From: yousefed Date: Wed, 21 Jun 2023 18:29:40 +0200 Subject: [PATCH 071/362] keep original imports --- src/utils/DeleteSet.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 6d2a43d42..d3b3ad757 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -1,26 +1,18 @@ import { - DSDecoderV1, - DSDecoderV2, - DSEncoderV1, - DSEncoderV2, - GC, - ID // eslint-disable-line - , - Item, - StructStore, Transaction, - UpdateEncoderV2, findIndexSS, getState, + splitItem, iterateStructs, - splitItem + UpdateEncoderV2, + DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' -import * as decoding from 'lib0/decoding' -import * as encoding from 'lib0/encoding' -import * as map from 'lib0/map' import * as math from 'lib0/math' +import * as map from 'lib0/map' +import * as encoding from 'lib0/encoding' +import * as decoding from 'lib0/decoding' export class DeleteItem { /** From 942c8a267be05b0b1156f9be8d62347ae33dcad2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 22 Jun 2023 17:46:49 +0200 Subject: [PATCH 072/362] remove duplicate Transaction.callAll logic --- src/utils/Transaction.js | 47 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index a7b23ff8e..359405955 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -275,31 +275,30 @@ const cleanupTransactions = (transactionCleanups, i) => { ) fs.push(() => { // deep observe events - transaction.changedParentTypes.forEach((events, type) => - fs.push(() => { - // We need to think about the possibility that the user transforms the - // Y.Doc in the event. - if (type._item === null || !type._item.deleted) { - events = events - .filter(event => - event.target._item === null || !event.target._item.deleted - ) - events - .forEach(event => { - event.currentTarget = type - event._path = null - }) - // sort events by path length so that top-level events are fired first. - events - .sort((event1, event2) => event1.path.length - event2.path.length) - // We don't need to check for events.length - // because we know it has at least one element - callEventHandlerListeners(type._dEH, events, transaction) - } - }) - ) - fs.push(() => doc.emit('afterTransaction', [transaction, doc])) + transaction.changedParentTypes.forEach((events, type) => { + // We need to think about the possibility that the user transforms the + // Y.Doc in the event. + if (type._dEH.l.length > 0 && (type._item === null || !type._item.deleted)) { + events = events + .filter(event => + event.target._item === null || !event.target._item.deleted + ) + events + .forEach(event => { + event.currentTarget = type + // path is relative to the current target + event._path = null + }) + // sort events by path length so that top-level events are fired first. + events + .sort((event1, event2) => event1.path.length - event2.path.length) + // We don't need to check for events.length + // because we know it has at least one element + callEventHandlerListeners(type._dEH, events, transaction) + } + }) }) + fs.push(() => doc.emit('afterTransaction', [transaction, doc])) callAll(fs, []) if (transaction._needFormattingCleanup) { cleanupYTextAfterTransaction(transaction) From b792902f17894f0e46e3f829074e31f5eafdc167 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 22 Jun 2023 17:55:45 +0200 Subject: [PATCH 073/362] 13.6.5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8f0cae4e..bbbd47b34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.4", + "version": "13.6.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.4", + "version": "13.6.5", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index f2273225f..4239b88df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.4", + "version": "13.6.5", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 981340139fc0b4601b74392e2adf6b67a4343002 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 25 Jun 2023 12:46:02 +0200 Subject: [PATCH 074/362] skip iterating when there are no formatting items - replaces #547 --- src/structs/ContentFormat.js | 18 ++++++++++-------- src/types/YText.js | 11 ++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/structs/ContentFormat.js b/src/structs/ContentFormat.js index 6ac4c29c8..dbc06a537 100644 --- a/src/structs/ContentFormat.js +++ b/src/structs/ContentFormat.js @@ -1,6 +1,6 @@ import { - AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Item, StructStore, Transaction // eslint-disable-line + YText, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Item, StructStore, Transaction // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -47,28 +47,30 @@ export class ContentFormat { } /** - * @param {number} offset + * @param {number} _offset * @return {ContentFormat} */ - splice (offset) { + splice (_offset) { throw error.methodUnimplemented() } /** - * @param {ContentFormat} right + * @param {ContentFormat} _right * @return {boolean} */ - mergeWith (right) { + mergeWith (_right) { return false } /** - * @param {Transaction} transaction + * @param {Transaction} _transaction * @param {Item} item */ - integrate (transaction, item) { + integrate (_transaction, item) { // @todo searchmarker are currently unsupported for rich text documents - /** @type {AbstractType} */ (item.parent)._searchMarker = null + const p = /** @type {YText} */ (item.parent) + p._searchMarker = null + p._hasFormatting = true } /** diff --git a/src/types/YText.js b/src/types/YText.js index 79430c1a5..399a6ff36 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -505,7 +505,7 @@ export const cleanupYTextAfterTransaction = transaction => { // cleanup in a new transaction transact(doc, (t) => { iterateDeletedStructs(transaction, transaction.deleteSet, item => { - if (item instanceof GC || needFullCleanup.has(/** @type {YText} */ (item.parent))) { + if (item instanceof GC || !(/** @type {YText} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YText} */ (item.parent))) { return } const parent = /** @type {YText} */ (item.parent) @@ -859,9 +859,14 @@ export class YText extends AbstractType { */ this._pending = string !== undefined ? [() => this.insert(0, string)] : [] /** - * @type {Array} + * @type {Array|null} */ this._searchMarker = [] + /** + * Whether this YText contains formatting attributes. + * This flag is updated when a formatting item is integrated (see ContentFormat.integrate) + */ + this._hasFormatting = false } /** @@ -911,7 +916,7 @@ export class YText extends AbstractType { const event = new YTextEvent(this, transaction, parentSubs) callTypeObservers(this, transaction, event) // If a remote change happened, we try to cleanup potential formatting duplicates. - if (!transaction.local) { + if (!transaction.local && this._hasFormatting) { transaction._needFormattingCleanup = true } } From 8586806932e65b2c9957f5e4ecd74547baf301ad Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 25 Jun 2023 19:10:34 +0200 Subject: [PATCH 075/362] 13.6.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index bbbd47b34..1be1b8fd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.5", + "version": "13.6.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.5", + "version": "13.6.6", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index 4239b88df..df55ca5c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.5", + "version": "13.6.6", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 90f2a06b5e892ac08e989f5809e2e371ee101ec4 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 27 Jun 2023 13:20:53 +0200 Subject: [PATCH 076/362] throw error when event changes are computed after a transaction --- src/utils/YEvent.js | 9 +++++++++ tests/y-map.tests.js | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 8aafd94f6..b47d8c9ad 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -6,6 +6,9 @@ import { import * as set from 'lib0/set' import * as array from 'lib0/array' +import * as error from 'lib0/error' + +const errorComputeChanges = 'You must not compute changes after the event-handler fired.' /** * @template {AbstractType} T @@ -84,6 +87,9 @@ export class YEvent { */ get keys () { if (this._keys === null) { + if (this.transaction.doc._transactionCleanups.length === 0) { + throw error.create(errorComputeChanges) + } const keys = new Map() const target = this.target const changed = /** @type Set */ (this.transaction.changed.get(target)) @@ -167,6 +173,9 @@ export class YEvent { get changes () { let changes = this._changes if (changes === null) { + if (this.transaction.doc._transactionCleanups.length === 0) { + throw error.create(errorComputeChanges) + } const target = this.target const added = set.create() const deleted = set.create() diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 3356afc12..346bd746f 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -8,6 +8,29 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' +/** + * Computing event changes after transaction should result in an error. See yjs#539 + * + * @param {t.TestCase} _tc + */ +export const testMapEventError = _tc => { + const doc = new Y.Doc() + const ymap = doc.getMap() + /** + * @type {any} + */ + let event = null + ymap.observe((e) => { + event = e + }) + t.fails(() => { + t.info(event.keys) + }) + t.fails(() => { + t.info(event.keys) + }) +} + /** * @param {t.TestCase} tc */ From c77dedb68ddc71124df901d119eb22664537047e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 17 Jul 2023 14:29:54 +0200 Subject: [PATCH 077/362] bulk-merging structs - replaces #542, fixes #541 --- src/structs/ContentType.js | 4 ++-- src/utils/Transaction.js | 43 +++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index 96a731de5..e9c11de10 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -108,7 +108,7 @@ export class ContentType { while (item !== null) { if (!item.deleted) { item.delete(transaction) - } else { + } else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) { // This will be gc'd later and we want to merge it if possible // We try to merge all deleted items after each transaction, // but we have no knowledge about that this needs to be merged @@ -120,7 +120,7 @@ export class ContentType { this.type._map.forEach(item => { if (!item.deleted) { item.delete(transaction) - } else { + } else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) { // same as above transaction._mergeStructs.push(item) } diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 359405955..5b93369ff 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -166,18 +166,29 @@ export const addChangedTypeToTransaction = (transaction, type, parentSub) => { /** * @param {Array} structs * @param {number} pos + * @return {number} # of merged structs */ -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 && /** @type {AbstractType} */ (right.parent)._map.get(right.parentSub) === right) { - /** @type {AbstractType} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left)) +const tryToMergeWithLefts = (structs, pos) => { + let right = structs[pos] + let left = structs[pos - 1] + let i = pos + for (; i > 0; right = left, left = structs[--i - 1]) { + if (left.deleted === right.deleted && left.constructor === right.constructor) { + if (left.mergeWith(right)) { + if (right instanceof Item && right.parentSub !== null && /** @type {AbstractType} */ (right.parent)._map.get(right.parentSub) === right) { + /** @type {AbstractType} */ (right.parent)._map.set(right.parentSub, /** @type {Item} */ (left)) + } + continue } } + break + } + const merged = pos - i + if (merged) { + // remove all merged structs from the array + structs.splice(pos + 1 - merged, merged) } + return merged } /** @@ -224,9 +235,9 @@ const tryMergeDeleteSet = (ds, store) => { for ( let si = mostRightIndexToCheck, struct = structs[si]; si > 0 && struct.id.clock >= deleteItem.clock; - struct = structs[--si] + struct = structs[si] ) { - tryToMergeWithLeft(structs, si) + si -= 1 + tryToMergeWithLefts(structs, si) } } }) @@ -318,23 +329,25 @@ const cleanupTransactions = (transactionCleanups, i) => { const structs = /** @type {Array} */ (store.clients.get(client)) // we iterate from right to left so we can safely remove entries const firstChangePos = math.max(findIndexSS(structs, beforeClock), 1) - for (let i = structs.length - 1; i >= firstChangePos; i--) { - tryToMergeWithLeft(structs, i) + for (let i = structs.length - 1; i >= firstChangePos;) { + i -= 1 + tryToMergeWithLefts(structs, i) } } }) // try to merge mergeStructs // @todo: it makes more sense to transform mergeStructs to a DS, sort it, and merge from right to left // but at the moment DS does not handle duplicates - for (let i = 0; i < mergeStructs.length; i++) { + for (let i = mergeStructs.length - 1; i >= 0; i--) { const { client, clock } = mergeStructs[i].id const structs = /** @type {Array} */ (store.clients.get(client)) const replacedStructPos = findIndexSS(structs, clock) if (replacedStructPos + 1 < structs.length) { - tryToMergeWithLeft(structs, replacedStructPos + 1) + if (tryToMergeWithLefts(structs, replacedStructPos + 1) > 1) { + continue // no need to perform next check, both are already merged + } } if (replacedStructPos > 0) { - tryToMergeWithLeft(structs, replacedStructPos) + tryToMergeWithLefts(structs, replacedStructPos) } } if (!transaction.local && transaction.afterState.get(doc.clientID) !== transaction.beforeState.get(doc.clientID)) { From 5ee6992d1fce68993b9a6ea216358d27fc29b294 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 17 Jul 2023 14:43:07 +0200 Subject: [PATCH 078/362] 13.6.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1be1b8fd7..b29195775 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.6", + "version": "13.6.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.6", + "version": "13.6.7", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index df55ca5c7..dd8eacb06 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.6", + "version": "13.6.7", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 03b9a806e86732b35052c6e00491719ce093b2a7 Mon Sep 17 00:00:00 2001 From: Braden Date: Sun, 23 Jul 2023 14:40:03 -0500 Subject: [PATCH 079/362] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e1ee09030..4cda4a650 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 sharing analyses, documentation, spreadsheets, and dashboards. * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon footprint calculator has a group P2P mode based on yjs +* [LegendKeeper](https://legendkeeper.com) Collaborative campaign planner and worldbuilding app for tabletop RPGs. ## Table of Contents From 7bdf94167afea659d9df4a6ad9619aa93a7ad442 Mon Sep 17 00:00:00 2001 From: GQ Date: Mon, 31 Jul 2023 15:05:33 +0800 Subject: [PATCH 080/362] fix comment --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1ee09030..d1accf249 100644 --- a/README.md +++ b/README.md @@ -1033,7 +1033,7 @@ doc.transact(() => { ytext.insert(0, 'abc') }, 41) undoManager.undo() -ytext.toString() // => '' (not tracked because 41 is not an instance of +ytext.toString() // => 'abc' (not tracked because 41 is not an instance of // `trackedTransactionorigins`) ytext.delete(0, 3) // revert change From 87b7d3e9515d82c34dc347740945297e7fc4217d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 23 Aug 2023 15:42:48 +0200 Subject: [PATCH 081/362] add Yjs-compatible ports to documentation --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index e1ee09030..7103c6668 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Overview](#Overview) * [Bindings](#Bindings) * [Providers](#Providers) + * [Ports](#Ports) * [Getting Started](#Getting-Started) * [API](#API) * [Shared Types](#Shared-Types) @@ -153,6 +154,22 @@ y-websocket provider. +# Ports + +There are several Yjs-compatible ports to other programming languages. + +* [y-octo](https://github.com/toeverything/y-octo) - Rust implementation by +Affine +* [y-crdt](https://github.com/y-crdt/y-crdt) - Rust implementation with multiple +language bindings to other languages + * [yrs](https://github.com/y-crdt/y-crdt/tree/main/yrs) - Rust interface + * [ypy](https://github.com/y-crdt/ypy) - Python binding + * [yrb](https://github.com/y-crdt/yrb) - Ruby binding + * [yrb](https://github.com/y-crdt/yswift) - Swift binding + * [yffi](https://github.com/y-crdt/y-crdt/tree/main/yffi) - C-FFI + * [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) - WASM binding +* [ycs](https://github.com/yjs/ycs) - .Net compatible C# implementation. + ## Getting Started Install Yjs and a provider with your favorite package manager: From bd867cb1610af8b56bed7d98db14c86251a460bd Mon Sep 17 00:00:00 2001 From: Akshay Kumar Date: Wed, 23 Aug 2023 19:31:58 +0530 Subject: [PATCH 082/362] add oorja to who is using yjs --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7103c6668..b38994577 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 sharing analyses, documentation, spreadsheets, and dashboards. * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon footprint calculator has a group P2P mode based on yjs +* [oorja.io](https://oorja.io) Online meeting spaces extensible with collaborative apps, end-to-end encrypted. + ## Table of Contents From 61abf3a1db4a0edcb3b39f7acb28bf6893f1bc7b Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Wed, 23 Aug 2023 09:25:18 -0500 Subject: [PATCH 083/362] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7103c6668..6305c3b59 100644 --- a/README.md +++ b/README.md @@ -159,7 +159,7 @@ y-websocket provider. There are several Yjs-compatible ports to other programming languages. * [y-octo](https://github.com/toeverything/y-octo) - Rust implementation by -Affine +[AFFiNE](https://affine.pro) * [y-crdt](https://github.com/y-crdt/y-crdt) - Rust implementation with multiple language bindings to other languages * [yrs](https://github.com/y-crdt/y-crdt/tree/main/yrs) - Rust interface From 97c09a6cca0e1bf5c7196714a6660de497d15e94 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Aug 2023 13:52:38 +0200 Subject: [PATCH 084/362] fix #509 --- src/utils/UndoManager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 746d0f010..f6f13ee58 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -15,6 +15,7 @@ import { import * as time from 'lib0/time' import * as array from 'lib0/array' +import * as logging from 'lib0/logging' import { Observable } from 'lib0/observable' export class StackItem { @@ -169,6 +170,7 @@ export class UndoManager extends Observable { * @type {Array>} */ this.scope = [] + this.doc = doc this.addToScope(typeScope) this.deleteFilter = deleteFilter trackedOrigins.add(this) @@ -189,7 +191,6 @@ export class UndoManager extends Observable { */ this.undoing = false this.redoing = false - this.doc = doc this.lastChange = 0 this.ignoreRemoteMapChanges = ignoreRemoteMapChanges this.captureTimeout = captureTimeout @@ -263,6 +264,7 @@ export class UndoManager extends Observable { 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 this.scope.push(ytype) } }) From 9a9a1ffeeba20cb08fcad3974f5b516e316e8b9e Mon Sep 17 00:00:00 2001 From: Greg Werner Date: Mon, 28 Aug 2023 14:45:43 -0400 Subject: [PATCH 085/362] Adds IllumiDesk as a reference to the using list Signed-off-by: Greg Werner --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 173ad0f0f..4cec0d7d8 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 +* [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I. :brain: * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: * [Sana](https://sanalabs.com/) A learning platform with collaborative text editing powered by Yjs. From eeae74decf7e5ad1c3a1ed35f9a76e50fafc093a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 29 Aug 2023 16:34:26 +0200 Subject: [PATCH 086/362] sponsors update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cec0d7d8..1418388f9 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,6 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 -* [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I. :brain: * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: * [Sana](https://sanalabs.com/) A learning platform with collaborative text editing powered by Yjs. @@ -63,6 +62,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 footprint calculator has a group P2P mode based on yjs * [oorja.io](https://oorja.io) Online meeting spaces extensible with collaborative apps, end-to-end encrypted. * [LegendKeeper](https://legendkeeper.com) Collaborative campaign planner and worldbuilding app for tabletop RPGs. +* [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I. ## Table of Contents From 0b30413f6e9d2494e8066fbb8232ec0b3f922e67 Mon Sep 17 00:00:00 2001 From: Steven Fabre Date: Thu, 31 Aug 2023 05:34:05 -0400 Subject: [PATCH 087/362] Liveblocks in Yjs README Signed-off-by: Steven Fabre --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 421696fde..2c62fcf56 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,10 @@ Also includes a peer-sync mechanism to catch up on missed updates. an append-only log of CRDT local updates (hypercore). Multifeed manages and sync hypercores and y-dat listens to changes and applies them to the Yjs document. +
@liveblocks/yjs
+
+Liveblocks Yjs provides a fully hosted WebSocket infrastructure and persisted data store for Yjs documents. No configuration or maintenance is required. It also features Yjs webhook events, REST API to read and update Yjs documents, and a browser DevTools extension. +
Matrix-CRDT
Use Matrix as an off-the-shelf backend for From 4d7a366f6e41e05cd4e5545b86621ee51f7ce092 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Thu, 31 Aug 2023 17:18:52 -0500 Subject: [PATCH 088/362] docs: add `@toeverything/y-indexeddb` --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index e1ee09030..15083bf91 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,11 @@ Encryption (E2EE).
Adds persistent storage to a server with MongoDB. Can be used with the y-websocket provider. +
+
+@toeverything/y-indexeddb
+
+Like y-indexeddb, but with sub-documents support and fully TypeScript.
From a1fda219e4af6466e2446e71aaa8afe050cd47fc Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 1 Sep 2023 13:26:04 +0200 Subject: [PATCH 089/362] lint readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7661b8e3f..547c33e70 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,12 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 sharing analyses, documentation, spreadsheets, and dashboards. * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon footprint calculator has a group P2P mode based on yjs -* [oorja.io](https://oorja.io) Online meeting spaces extensible with collaborative apps, end-to-end encrypted. -* [LegendKeeper](https://legendkeeper.com) Collaborative campaign planner and worldbuilding app for tabletop RPGs. +* [oorja.io](https://oorja.io) Online meeting spaces extensible with + collaborative apps, end-to-end encrypted. +* [LegendKeeper](https://legendkeeper.com) Collaborative campaign planner and + worldbuilding app for tabletop RPGs. * [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I. - ## Table of Contents * [Overview](#Overview) From a099e98bd66c85b5a1b2c0c9aa561886b6c2bc67 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 7 Sep 2023 13:41:35 +0200 Subject: [PATCH 090/362] create error on call - fixes #569 --- README.md | 6 +++++- src/types/AbstractType.js | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 35bfbf6bb..f15e5ea54 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,11 @@ hypercores and y-dat listens to changes and applies them to the Yjs document.
@liveblocks/yjs
-Liveblocks Yjs provides a fully hosted WebSocket infrastructure and persisted data store for Yjs documents. No configuration or maintenance is required. It also features Yjs webhook events, REST API to read and update Yjs documents, and a browser DevTools extension. +Liveblocks Yjs provides a fully +hosted WebSocket infrastructure and persisted data store for Yjs +documents. No configuration or maintenance is required. It also features +Yjs webhook events, REST API to read and update Yjs documents, and a +browser DevTools extension.
Matrix-CRDT
diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 144cfc3cd..3163b8da8 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -683,7 +683,7 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem, packJsonContent() } -const lengthExceeded = error.create('Length exceeded!') +const lengthExceeded = () => error.create('Length exceeded!') /** * @param {Transaction} transaction @@ -696,7 +696,7 @@ const lengthExceeded = error.create('Length exceeded!') */ export const typeListInsertGenerics = (transaction, parent, index, content) => { if (index > parent._length) { - throw lengthExceeded + throw lengthExceeded() } if (index === 0) { if (parent._searchMarker) { @@ -798,7 +798,7 @@ export const typeListDelete = (transaction, parent, index, length) => { n = n.right } if (length > 0) { - throw lengthExceeded + throw lengthExceeded() } if (parent._searchMarker) { updateMarkerChanges(parent._searchMarker, startIndex, -startLength + length /* in case we remove the above exception */) From 29270b5f3ee625f6b5ab9c97f70705f267c9e2d7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 18 Sep 2023 09:55:50 +0200 Subject: [PATCH 091/362] fix "can't read origin of undefined" - fixes #417 --- src/structs/Item.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/structs/Item.js b/src/structs/Item.js index 7e1bc92cf..c14778b37 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -389,9 +389,8 @@ export class Item extends AbstractStruct { } if ((this.left && this.left.constructor === GC) || (this.right && this.right.constructor === GC)) { this.parent = null - } - // only set parent if this shouldn't be garbage collected - if (!this.parent) { + } else if (!this.parent) { + // only set parent if this shouldn't be garbage collected if (this.left && this.left.constructor === Item) { this.parent = this.left.parent this.parentSub = this.left.parentSub From 2fe8907ab0a2e07f5e735e895efbb15dbb751283 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 18 Sep 2023 10:22:28 +0200 Subject: [PATCH 092/362] 13.6.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b29195775..f6b9c06c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.7", + "version": "13.6.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.7", + "version": "13.6.8", "license": "MIT", "dependencies": { "lib0": "^0.2.74" diff --git a/package.json b/package.json index dd8eacb06..fadc72c22 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.7", + "version": "13.6.8", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 3a758f89a1a1f5464ee089bac745f777a3ba17ca Mon Sep 17 00:00:00 2001 From: Eric Hasegawa Date: Mon, 18 Sep 2023 13:30:15 -0700 Subject: [PATCH 093/362] Correct typos in INTERNALS.md --- INTERNALS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INTERNALS.md b/INTERNALS.md index a7874076f..4daf96594 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -149,8 +149,8 @@ concepts that can be used to create a custom network protocol: * `update`: The Yjs document can be encoded to an *update* object that can be parsed to reconstruct the document. Also every change on the document fires -an incremental document updates that allows clients to sync with each other. -The update object is an Uint8Array that efficiently encodes `Item` objects and +an incremental document update that allows clients to sync with each other. +The update object is a Uint8Array that efficiently encodes `Item` objects and the delete set. * `state vector`: A state vector defines the known state of each user (a set of tuples `(client, clock)`). This object is also efficiently encoded as a From 9a7b659919f6d603b1a8cc87b8dddcf5436e7ac9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 23 Sep 2023 16:59:23 +0200 Subject: [PATCH 094/362] bump lib0 --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6b9c06c8..2e7e78799 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.6.8", "license": "MIT", "dependencies": { - "lib0": "^0.2.74" + "lib0": "^0.2.86" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", @@ -2481,9 +2481,9 @@ } }, "node_modules/lib0": { - "version": "0.2.74", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.74.tgz", - "integrity": "sha512-roj9i46/JwG5ik5KNTkxP2IytlnrssAkD/OhlAVtE+GqectrdkfR+pttszVLrOzMDeXNs1MPt6yo66MUolWSiA==", + "version": "0.2.86", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.86.tgz", + "integrity": "sha512-kxigQTM4Q7NwJkEgdqQvU21qiR37twcqqLmh+/SbiGbRLfPlLVbHyY9sWp7PwXh0Xus9ELDSjsUOwcrdt5yZ4w==", "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -2492,7 +2492,7 @@ "0serve": "bin/0serve.js" }, "engines": { - "node": ">=14" + "node": ">=16" }, "funding": { "type": "GitHub Sponsors ❤", diff --git a/package.json b/package.json index fadc72c22..eaeac95b5 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.74" + "lib0": "^0.2.86" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", From 171d801e0a9167ec0485fda530a26c35b8a56b50 Mon Sep 17 00:00:00 2001 From: Jesse Jackson Date: Thu, 5 Oct 2023 00:41:48 -0500 Subject: [PATCH 095/362] docs(readme): fix typo Update link text for Swift bindings: from "yrb" to "yswift" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 199e926e0..5ded062b5 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ language bindings to other languages * [yrs](https://github.com/y-crdt/y-crdt/tree/main/yrs) - Rust interface * [ypy](https://github.com/y-crdt/ypy) - Python binding * [yrb](https://github.com/y-crdt/yrb) - Ruby binding - * [yrb](https://github.com/y-crdt/yswift) - Swift binding + * [yswift](https://github.com/y-crdt/yswift) - Swift binding * [yffi](https://github.com/y-crdt/y-crdt/tree/main/yffi) - C-FFI * [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) - WASM binding * [ycs](https://github.com/yjs/ycs) - .Net compatible C# implementation. From e6afc51b849e2d40b483107b2d74ac96095827ef Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Sat, 7 Oct 2023 20:46:52 +0200 Subject: [PATCH 096/362] add documentation on V2 events --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 199e926e0..a33cbc651 100644 --- a/README.md +++ b/README.md @@ -697,7 +697,8 @@ type. Doesn't log types that have not been defined (using on('update', function(updateMessage:Uint8Array, origin:any, Y.Doc):void)
Listen to document updates. Document updates must be transmitted to all other -peers. You can apply document updates in any order and multiple times. +peers. You can apply document updates in any order and multiple times. Use `updateV2` +to receive V2 events.
on('beforeTransaction', function(Y.Transaction, Y.Doc):void)
Emitted before each transaction.
@@ -822,8 +823,10 @@ Yjs implements two update formats. By default you are using the V1 update format You can opt-in into the V2 update format wich provides much better compression. It is not yet used by all providers. However, you can already use it if you are building your own provider. All below functions are available with the -suffix "V2". E.g. `Y.applyUpdate` ⇒ `Y.applyUpdateV2`. We also support conversion -functions between both formats: `Y.convertUpdateFormatV1ToV2` & `Y.convertUpdateFormatV2ToV1`. +suffix "V2". E.g. `Y.applyUpdate` ⇒ `Y.applyUpdateV2`. Also when listening to updates +you need to specifically need listen for V2 events e.g. `yDoc.on('updateV2', …)`. +We also support conversion functions between both formats: +`Y.convertUpdateFormatV1ToV2` & `Y.convertUpdateFormatV2ToV1`. #### Update API From e7572d61c6b3334bdfd77fe2100b0372d1d56107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 22 Oct 2023 11:03:25 +0200 Subject: [PATCH 097/362] Only emit "load" when sync is set to true --- src/utils/Doc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index d07dd56ee..ddde1d543 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -113,7 +113,7 @@ export class Doc extends Observable { this.whenSynced = provideSyncedPromise() } this.isSynced = isSynced === undefined || isSynced === true - if (!this.isLoaded) { + if (this.isSynced && !this.isLoaded) { this.emit('load', []) } }) From 25bef2308fc8ae2de0e762ce8b50fc8a908e1045 Mon Sep 17 00:00:00 2001 From: Andrew Haines Date: Wed, 25 Oct 2023 15:00:48 +0100 Subject: [PATCH 098/362] Fix typing of `Y.Map` iterators Signed-off-by: Andrew Haines --- src/types/YMap.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/types/YMap.js b/src/types/YMap.js index e2dd7a496..3e1a975e5 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -41,7 +41,7 @@ export class YMapEvent extends YEvent { * A shared Map implementation. * * @extends AbstractType> - * @implements {Iterable} + * @implements {Iterable<[string, MapType]>} */ export class YMap extends AbstractType { /** @@ -152,7 +152,7 @@ export class YMap extends AbstractType { /** * Returns the values for each element in the YMap Type. * - * @return {IterableIterator} + * @return {IterableIterator} */ values () { return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1]) @@ -161,10 +161,10 @@ export class YMap extends AbstractType { /** * Returns an Iterator of [key, value] pairs * - * @return {IterableIterator} + * @return {IterableIterator<[string, MapType]>} */ entries () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => [v[0], v[1].content.getContent()[v[1].length - 1]]) + return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]])) } /** @@ -183,7 +183,7 @@ export class YMap extends AbstractType { /** * Returns an Iterator of [key, value] pairs * - * @return {IterableIterator} + * @return {IterableIterator<[string, MapType]>} */ [Symbol.iterator] () { return this.entries() From f52569b8fa97c1e4c44599b06b160220b91f3f6a Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Mon, 30 Oct 2023 19:41:09 -0500 Subject: [PATCH 099/362] fix: remove unused if-statement check --- src/utils/encoding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 83e1b91fa..a86f7b427 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -251,7 +251,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { return nextStructsTarget } let curStructsTarget = getNextStructTarget() - if (curStructsTarget === null && stack.length === 0) { + if (curStructsTarget === null) { return null } From e5f286cf897c216553b2f00332b2705e13efc357 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Mon, 6 Nov 2023 16:49:33 -0600 Subject: [PATCH 100/362] feat: expose some types --- src/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/index.js b/src/index.js index 781e8eb48..be7ca25ee 100644 --- a/src/index.js +++ b/src/index.js @@ -18,8 +18,10 @@ export { Item, AbstractStruct, GC, + Skip, ContentBinary, ContentDeleted, + ContentDoc, ContentEmbed, ContentFormat, ContentJSON, @@ -93,6 +95,9 @@ export { obfuscateUpdate, obfuscateUpdateV2, UpdateEncoderV1, + UpdateEncoderV2, + UpdateDecoderV1, + UpdateDecoderV2, equalDeleteSets, snapshotContainsUpdate } from './internals.js' From a3d69bba729091121f7f58c1c0693c6736298dfe Mon Sep 17 00:00:00 2001 From: Siddhartha Gunti Date: Tue, 7 Nov 2023 18:54:22 +0530 Subject: [PATCH 101/362] Adding www.btw.so to the platforms who use Yjs Repo here: https://github.com/btw-so/btw --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 199e926e0..da0ed4f63 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [LegendKeeper](https://legendkeeper.com) Collaborative campaign planner and worldbuilding app for tabletop RPGs. * [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I. +* [btw](https://www.btw.so) Open-source Medium alternative ## Table of Contents From 37236fa31f9db3d9bd4d27aaba028713b2d1b98a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 20 Nov 2023 12:39:51 +0100 Subject: [PATCH 102/362] update license. closes #471 --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index c8c5c6da7..f55e8b484 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ The MIT License (MIT) -Copyright (c) 2014 - - Kevin Jahns . +Copyright (c) 2023 + - Kevin Jahns . - Chair of Computer Science 5 (Databases & Information Systems), RWTH Aachen University, Germany Permission is hereby granted, free of charge, to any person obtaining a copy From 9f8c55885f8cbacd0d5380a3dcd0d6129568f6ba Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 20 Nov 2023 12:46:12 +0100 Subject: [PATCH 103/362] update github workflow --- .github/workflows/node.js.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index d7aaca982..023ba5d24 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -16,16 +16,16 @@ jobs: strategy: matrix: - node-version: [16.x, 18.x] + node-version: [16.x, 20.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm run lint - - run: npm run test-extensive + - run: npm run test env: CI: true From c2e70764004d6e485da41fa8ca8a384c541a3de1 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 20 Nov 2023 12:53:58 +0100 Subject: [PATCH 104/362] add iterator type checks --- tests/y-map.tests.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 346bd746f..70b3e3b1b 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -8,6 +8,31 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' +/** + * @param {t.TestCase} _tc + */ +export const testIterators = _tc => { + const ydoc = new Y.Doc() + /** + * @type {Y.Map} + */ + const ymap = ydoc.getMap() + // we are only checking if the type assumptions are correct + /** + * @type {Array} + */ + const vals = Array.from(ymap.values()) + /** + * @type {Array<[string,number]>} + */ + const entries = Array.from(ymap.entries()) + /** + * @type {Array} + */ + const keys = Array.from(ymap.keys()) + console.log(vals, entries, keys) +} + /** * Computing event changes after transaction should result in an error. See yjs#539 * From 013b2b68868578b54ce88b37f5592dec538e0880 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 20 Nov 2023 12:56:27 +0100 Subject: [PATCH 105/362] 13.6.9 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2e7e78799..89a6a501c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.8", + "version": "13.6.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.8", + "version": "13.6.9", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index eaeac95b5..6fb61eece 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.8", + "version": "13.6.9", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2c0daeb0711042cc8f41f8c7010025516acff933 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Nov 2023 12:24:21 +0100 Subject: [PATCH 106/362] implement snapshot API for yxml.getAttributes. implements #543 --- src/index.js | 1 + src/types/AbstractType.js | 28 ++++++++++++++++++++++++++++ src/types/YXmlElement.js | 8 +++++--- tests/snapshot.tests.js | 15 +++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index be7ca25ee..ae96b747c 100644 --- a/src/index.js +++ b/src/index.js @@ -52,6 +52,7 @@ export { getItem, typeListToArraySnapshot, typeMapGetSnapshot, + typeMapGetAllSnapshot, createDocFromSnapshot, iterateDeletedStructs, applyUpdate, diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 3163b8da8..8aef5dc4d 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -925,6 +925,34 @@ export const typeMapGetSnapshot = (parent, key, snapshot) => { return v !== null && isVisible(v, snapshot) ? v.content.getContent()[v.length - 1] : undefined } +/** + * @param {AbstractType} parent + * @param {Snapshot} snapshot + * @return {Object|number|null|Array|string|Uint8Array|AbstractType|undefined>} + * + * @private + * @function + */ +export const typeMapGetAllSnapshot = (parent, snapshot) => { + /** + * @type {Object} + */ + const res = {} + parent._map.forEach((value, key) => { + /** + * @type {Item|null} + */ + let v = value + while (v !== null && (!snapshot.sv.has(v.id.client) || v.id.clock >= (snapshot.sv.get(v.id.client) || 0))) { + v = v.left + } + if (v !== null && isVisible(v, snapshot)) { + res[key] = v.content.getContent()[v.length - 1] + } + }) + return res +} + /** * @param {Map} map * @return {IterableIterator>} diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 92088cdd1..7b18be694 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -8,9 +8,10 @@ import { typeMapSet, typeMapGet, typeMapGetAll, + typeMapGetAllSnapshot, typeListForEach, YXmlElementRefID, - YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line + Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line } from '../internals.js' /** @@ -192,12 +193,13 @@ export class YXmlElement extends YXmlFragment { /** * Returns all attribute name/value pairs in a JSON Object. * + * @param {Snapshot} [snapshot] * @return {{ [Key in Extract]?: KV[Key]}} A JSON Object that describes the attributes. * * @public */ - getAttributes () { - return /** @type {any} */ (typeMapGetAll(this)) + getAttributes (snapshot) { + return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this)) } /** diff --git a/tests/snapshot.tests.js b/tests/snapshot.tests.js index d72e7ba74..4f3ecd47d 100644 --- a/tests/snapshot.tests.js +++ b/tests/snapshot.tests.js @@ -14,6 +14,21 @@ export const testBasic = _tc => { t.assert(restored.getText().toString() === 'world!') } +/** + * @param {t.TestCase} _tc + */ +export const testBasicXmlAttributes = _tc => { + const ydoc = new Y.Doc({ gc: false }) + const yxml = ydoc.getMap().set('el', new Y.XmlElement('div')) + const snapshot1 = Y.snapshot(ydoc) + yxml.setAttribute('a', '1') + const snapshot2 = Y.snapshot(ydoc) + yxml.setAttribute('a', '2') + t.compare(yxml.getAttributes(), { a: '2' }) + t.compare(yxml.getAttributes(snapshot2), { a: '1' }) + t.compare(yxml.getAttributes(snapshot1), {}) +} + /** * @param {t.TestCase} _tc */ From 1d4f2e5435116c2f437e6a2fefe2132fe5abaf81 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Nov 2023 12:29:49 +0100 Subject: [PATCH 107/362] 13.6.10 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89a6a501c..41934864d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.9", + "version": "13.6.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.9", + "version": "13.6.10", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 6fb61eece..bdefe3206 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.9", + "version": "13.6.10", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 28ccd5e0dda2c819c8c96e534b88714f6322de5c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Nov 2023 19:55:29 +0100 Subject: [PATCH 108/362] add providers (also mention some y-crdt based providers) --- README.md | 67 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 95ce9e29b..97e5557a5 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,19 @@ and storing shared data for offline usage is quite a hassle. **Providers** manage all that for you and are the perfect starting point for your collaborative app. +> This list of providers is incomplete. Please open PRs to add your providers to +> this list! + +#### Connection Providers +
+
y-websocket
+
+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. y-sweet and ypy-websocket (see below) are +compatible to the y-wesocket protocol. +
y-webrtc
Propagates document updates peer-to-peer using WebRTC. The peers exchange @@ -119,17 +131,22 @@ are available. Communication over the signaling servers can be encrypted by providing a shared secret, keeping the connection information and the shared document private.
-
y-websocket
+
@liveblocks/yjs
-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. +Liveblocks Yjs provides a fully +hosted WebSocket infrastructure and persisted data store for Yjs +documents. No configuration or maintenance is required. It also features +Yjs webhook events, REST API to read and update Yjs documents, and a +browser DevTools extension.
-
y-indexeddb
+
y-sweet
-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. +A standalone yjs server with persistence to S3 or filesystem. They offer a +cloud service as well. +
+
PartyKit
+
+Cloud service for building multiplayer apps.
y-libp2p
@@ -144,14 +161,6 @@ Also includes a peer-sync mechanism to catch up on missed updates. an append-only log of CRDT local updates (hypercore). Multifeed manages and sync hypercores and y-dat listens to changes and applies them to the Yjs document.
-
@liveblocks/yjs
-
-Liveblocks Yjs provides a fully -hosted WebSocket infrastructure and persisted data store for Yjs -documents. No configuration or maintenance is required. It also features -Yjs webhook events, REST API to read and update Yjs documents, and a -browser DevTools extension. -
Matrix-CRDT
Use Matrix as an off-the-shelf backend for @@ -160,17 +169,37 @@ Use Matrix as transport and storage of Yjs updates, so you can focus building your client app and Matrix can provide powerful features like Authentication, Authorization, Federation, hosting (self-hosting or SaaS) and even End-to-End Encryption (E2EE). -
+ +
yrb-actioncable
+
+An ActionCable companion for Yjs clients. There is a fitting +redis extension as well. +
+
ypy-websocket
+
+Websocket backend, written in Python. +
+
+ +#### Persistence Providers + +
+
y-indexeddb
+
+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. +
y-mongodb-provider
Adds persistent storage to a server with MongoDB. Can be used with the y-websocket provider. -
+
@toeverything/y-indexeddb
Like y-indexeddb, but with sub-documents support and fully TypeScript. -
+
# Ports From fe36ffd122a6f2384293098afd52d2c0025fce2a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 28 Nov 2023 16:22:37 +0100 Subject: [PATCH 109/362] add AWS Sagemaker, JupyterLab, JupyterCAD as users --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 97e5557a5..a9ecabaee 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [Slidebeamer](https://slidebeamer.com/) Presentation app. * [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys. * [Skiff](https://skiff.org/) Private, decentralized workspace. +* [JupyterLab](https://jupyter.org/) Collaborative computational Notebooks +* [JupyterCad](https://jupytercad.readthedocs.io/en/latest/) Extension to + JupyterLab that enables collaborative editing of 3d FreeCAD Models. * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon @@ -66,6 +69,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 worldbuilding app for tabletop RPGs. * [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I. * [btw](https://www.btw.so) Open-source Medium alternative +* [AWS SageMaker](https://aws.amazon.com/sagemaker/) Tools for building Machine + Learning Models ## Table of Contents From 221cb81dbf6eb114cd7d55ac2cb8966f14e27eed Mon Sep 17 00:00:00 2001 From: Kevin Barrett Date: Thu, 30 Nov 2023 16:51:23 -0500 Subject: [PATCH 110/362] add screen.garden as user --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a9ecabaee..75f9aca79 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [btw](https://www.btw.so) Open-source Medium alternative * [AWS SageMaker](https://aws.amazon.com/sagemaker/) Tools for building Machine Learning Models +* [screen.garden](https://screen.garden) Collaborative backend for PKM apps. ## Table of Contents From 77bd74127d6c6376fdc6bff20c9ee487704b5975 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 11 Dec 2023 16:37:23 +0100 Subject: [PATCH 111/362] Update who-is-using (Cargo.site) --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9ecabaee..74bac1439 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,12 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. 🏅 -* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2: -* [Sana](https://sanalabs.com/) A learning platform with collaborative text - editing powered by Yjs. +* [Cargo](https://cargo.site/) Site builder for designers and artists :star2: +* [Gitbook](https://gitbook.com) Knowledge management for technical teams :star2: +* [Evernote](https://evernote.com) Note-taking app :star2: +* [Lessonspace](https://thelessonspace.com) Enterprise platform for virtual + classrooms and online training :star2: +* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star: * [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and community. :star: * [Room.sh](https://room.sh/) A meeting application with integrated @@ -47,6 +50,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 Nimbus Web. :star: * [Pluxbox RadioManager](https://getradiomanager.com/) A web-based app to collaboratively organize radio broadcasts. :star: +* [Sana](https://sanalabs.com/) A learning platform with collaborative text + editing powered by Yjs. * [Serenity Notes](https://www.serenity.re/en/notes) End-to-end encrypted collaborative notes app. * [PRSM](https://prsm.uk/) Collaborative mind-mapping and system visualisation. *[(source)](https://github.com/micrology/prsm)* From cf78ce12b254569a134eeb342496745ee98fd695 Mon Sep 17 00:00:00 2001 From: lukasz jazwa Date: Thu, 14 Dec 2023 21:55:53 +0100 Subject: [PATCH 112/362] Updated readme.md with Professional Support section --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 74bac1439..6041a79df 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,11 @@ Otherwise you can find help on our community [discussion board](https://discuss. Please contribute to the project financially - especially if your company relies on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=d42f2d)](https://github.com/sponsors/dmonad) +## Professional Support + +* [Support Contract with the Maintainer](https://github.com/sponsors/dmonad) - By contributing financially to the open-source Yjs project, you can receive professional support directly from the author. This includes the opportunity for weekly video calls to discuss your specific challenges. +* [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in consulting and developing real-time collaborative editing solutions for visual apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and various data visualization types. Their expertise empowers developers to build engaging and interactive visual experiences leveraging the power of Yjs. See their work in action at [Visual Collaboration Showcase](https://yjs-diagram.synergy.codes/). + ## Who is using Yjs * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source From 0241fd3c4080cc9d22bf364dd9a01a400b723cc7 Mon Sep 17 00:00:00 2001 From: Javier Gonzalez Date: Sat, 23 Dec 2023 11:16:57 +0100 Subject: [PATCH 113/362] Update README.md with mobx-keystone binding --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 74bac1439..52cc1a89e 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ are implemented in separate modules. | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | | 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) | ### Providers From 7a8ca6eaa57bb328bf739157d60ffedef876f96f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 15 Jan 2024 14:04:03 +0100 Subject: [PATCH 114/362] add linear as a user of Yjs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4004a4348..7c6552ae0 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 * [btw](https://www.btw.so) Open-source Medium alternative * [AWS SageMaker](https://aws.amazon.com/sagemaker/) Tools for building Machine Learning Models +* [linear](https://linear.app) Streamline issues, projects, and product roadmaps. ## Table of Contents From 1cb52dc863e6fb6a7d87a775d9f7273b4404dbb7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 21 Jan 2024 11:27:12 +0100 Subject: [PATCH 115/362] fix Y.Text formatting issue - closes #606 --- README.md | 13 +++++++++-- src/internals.js | 1 - src/structs/AbstractStruct.js | 1 - src/structs/ContentDeleted.js | 1 - src/structs/ContentDoc.js | 1 - src/structs/ContentEmbed.js | 1 - src/structs/ContentFormat.js | 1 - src/structs/ContentType.js | 1 - src/structs/GC.js | 1 - src/structs/Item.js | 1 - src/structs/Skip.js | 1 - src/types/AbstractType.js | 1 - src/types/YMap.js | 1 - src/types/YText.js | 22 +++++++++--------- src/types/YXmlEvent.js | 1 - src/types/YXmlHook.js | 1 - src/types/YXmlText.js | 1 - src/utils/AbstractConnector.js | 1 - src/utils/DeleteSet.js | 1 - src/utils/ID.js | 1 - src/utils/PermanentUserData.js | 1 - src/utils/RelativePosition.js | 1 - src/utils/Snapshot.js | 1 - src/utils/StructStore.js | 1 - src/utils/Transaction.js | 1 - src/utils/UpdateEncoder.js | 1 - src/utils/YEvent.js | 1 - src/utils/encoding.js | 1 - src/utils/isParentOf.js | 1 - src/utils/logging.js | 1 - src/utils/updates.js | 1 - tests/compatibility.tests.js | 1 - tests/doc.tests.js | 1 - tests/relativePositions.tests.js | 1 - tests/testHelper.js | 1 - tests/undo-redo.tests.js | 40 ++++++++++++++++++++++++++++++++ 36 files changed, 62 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 7c6552ae0..6f85686b1 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,17 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2 ## Professional Support -* [Support Contract with the Maintainer](https://github.com/sponsors/dmonad) - By contributing financially to the open-source Yjs project, you can receive professional support directly from the author. This includes the opportunity for weekly video calls to discuss your specific challenges. -* [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in consulting and developing real-time collaborative editing solutions for visual apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and various data visualization types. Their expertise empowers developers to build engaging and interactive visual experiences leveraging the power of Yjs. See their work in action at [Visual Collaboration Showcase](https://yjs-diagram.synergy.codes/). +* [Support Contract with the Maintainer](https://github.com/sponsors/dmonad) - +By contributing financially to the open-source Yjs project, you can receive +professional support directly from the author. This includes the opportunity for +weekly video calls to discuss your specific challenges. +* [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in +consulting and developing real-time collaborative editing solutions for visual +apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and +various data visualization types. Their expertise empowers developers to build +engaging and interactive visual experiences leveraging the power of Yjs. See +their work in action at [Visual Collaboration +Showcase](https://yjs-diagram.synergy.codes/). ## Who is using Yjs diff --git a/src/internals.js b/src/internals.js index bc386f0a8..cb2fcac8c 100644 --- a/src/internals.js +++ b/src/internals.js @@ -1,4 +1,3 @@ - export * from './utils/AbstractConnector.js' export * from './utils/DeleteSet.js' export * from './utils/Doc.js' diff --git a/src/structs/AbstractStruct.js b/src/structs/AbstractStruct.js index 38457aef7..ad0000539 100644 --- a/src/structs/AbstractStruct.js +++ b/src/structs/AbstractStruct.js @@ -1,4 +1,3 @@ - import { UpdateEncoderV1, UpdateEncoderV2, ID, Transaction // eslint-disable-line } from '../internals.js' diff --git a/src/structs/ContentDeleted.js b/src/structs/ContentDeleted.js index 7225e1f61..917ba2474 100644 --- a/src/structs/ContentDeleted.js +++ b/src/structs/ContentDeleted.js @@ -1,4 +1,3 @@ - import { addToDeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line diff --git a/src/structs/ContentDoc.js b/src/structs/ContentDoc.js index 2c3bf8a69..15836f515 100644 --- a/src/structs/ContentDoc.js +++ b/src/structs/ContentDoc.js @@ -1,4 +1,3 @@ - import { Doc, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item // eslint-disable-line } from '../internals.js' diff --git a/src/structs/ContentEmbed.js b/src/structs/ContentEmbed.js index a64c53fa4..46fba375d 100644 --- a/src/structs/ContentEmbed.js +++ b/src/structs/ContentEmbed.js @@ -1,4 +1,3 @@ - import { UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line } from '../internals.js' diff --git a/src/structs/ContentFormat.js b/src/structs/ContentFormat.js index dbc06a537..eb2bd0eed 100644 --- a/src/structs/ContentFormat.js +++ b/src/structs/ContentFormat.js @@ -1,4 +1,3 @@ - import { YText, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Item, StructStore, Transaction // eslint-disable-line } from '../internals.js' diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index e9c11de10..630efeb36 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -1,4 +1,3 @@ - import { readYArray, readYMap, diff --git a/src/structs/GC.js b/src/structs/GC.js index 42d71f737..3c7cec0c3 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -1,4 +1,3 @@ - import { AbstractStruct, addStruct, diff --git a/src/structs/Item.js b/src/structs/Item.js index c14778b37..1b7ba9395 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -1,4 +1,3 @@ - import { GC, getState, diff --git a/src/structs/Skip.js b/src/structs/Skip.js index 3db2399d6..3f7caafa3 100644 --- a/src/structs/Skip.js +++ b/src/structs/Skip.js @@ -1,4 +1,3 @@ - import { AbstractStruct, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID // eslint-disable-line diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 8aef5dc4d..4cc1bf8a1 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -1,4 +1,3 @@ - import { removeEventHandlerListener, callEventHandlerListeners, diff --git a/src/types/YMap.js b/src/types/YMap.js index 3e1a975e5..855ccb4f1 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -1,4 +1,3 @@ - /** * @module YMap */ diff --git a/src/types/YText.js b/src/types/YText.js index 399a6ff36..c02ae6e56 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1,4 +1,3 @@ - /** * @module YText */ @@ -118,14 +117,15 @@ const findNextPosition = (transaction, pos, count) => { * @param {Transaction} transaction * @param {AbstractType} parent * @param {number} index + * @param {boolean} useSearchMarker * @return {ItemTextListPosition} * * @private * @function */ -const findPosition = (transaction, parent, index) => { +const findPosition = (transaction, parent, index, useSearchMarker) => { const currentAttributes = new Map() - const marker = findMarker(parent, index) + const marker = useSearchMarker ? findMarker(parent, index) : null if (marker) { const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes) return findNextPosition(transaction, pos, index - marker.index) @@ -1120,7 +1120,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - const pos = findPosition(transaction, this, index) + const pos = findPosition(transaction, this, index, !attributes) if (!attributes) { attributes = {} // @ts-ignore @@ -1138,20 +1138,20 @@ export class YText extends AbstractType { * * @param {number} index The index to insert the embed at. * @param {Object | AbstractType} embed The Object that represents the embed. - * @param {TextAttributes} attributes Attribute information to apply on the + * @param {TextAttributes} [attributes] Attribute information to apply on the * embed * * @public */ - insertEmbed (index, embed, attributes = {}) { + insertEmbed (index, embed, attributes) { const y = this.doc if (y !== null) { transact(y, transaction => { - const pos = findPosition(transaction, this, index) - insertText(transaction, this, pos, embed, attributes) + const pos = findPosition(transaction, this, index, !attributes) + insertText(transaction, this, pos, embed, attributes || {}) }) } else { - /** @type {Array} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes)) + /** @type {Array} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes || {})) } } @@ -1170,7 +1170,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - deleteText(transaction, findPosition(transaction, this, index), length) + deleteText(transaction, findPosition(transaction, this, index, true), length) }) } else { /** @type {Array} */ (this._pending).push(() => this.delete(index, length)) @@ -1194,7 +1194,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - const pos = findPosition(transaction, this, index) + const pos = findPosition(transaction, this, index, false) if (pos.right === null) { return } diff --git a/src/types/YXmlEvent.js b/src/types/YXmlEvent.js index 3c2566ed4..022b72d58 100644 --- a/src/types/YXmlEvent.js +++ b/src/types/YXmlEvent.js @@ -1,4 +1,3 @@ - import { YEvent, YXmlText, YXmlElement, YXmlFragment, Transaction // eslint-disable-line diff --git a/src/types/YXmlHook.js b/src/types/YXmlHook.js index be8c759b6..c5b5ed6dc 100644 --- a/src/types/YXmlHook.js +++ b/src/types/YXmlHook.js @@ -1,4 +1,3 @@ - import { YMap, YXmlHookRefID, diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index 470ce70f1..413b247cd 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -1,4 +1,3 @@ - import { YText, YXmlTextRefID, diff --git a/src/utils/AbstractConnector.js b/src/utils/AbstractConnector.js index ecf76a3bb..5f5446844 100644 --- a/src/utils/AbstractConnector.js +++ b/src/utils/AbstractConnector.js @@ -1,4 +1,3 @@ - import { Observable } from 'lib0/observable' import { diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index d3b3ad757..fe07b7ceb 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -1,4 +1,3 @@ - import { findIndexSS, getState, diff --git a/src/utils/ID.js b/src/utils/ID.js index 225ee5b4c..b0cabd8f3 100644 --- a/src/utils/ID.js +++ b/src/utils/ID.js @@ -1,4 +1,3 @@ - import { AbstractType } from '../internals.js' // eslint-disable-line import * as decoding from 'lib0/decoding' diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js index d9e44f129..80b712590 100644 --- a/src/utils/PermanentUserData.js +++ b/src/utils/PermanentUserData.js @@ -1,4 +1,3 @@ - import { YArray, YMap, diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index 614c0bc54..a1b4356b3 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -1,4 +1,3 @@ - import { writeID, readID, diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index dfd82c864..777cd39d3 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -1,4 +1,3 @@ - import { isDeleted, createDeleteSetFromStructStore, diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 7a2e256c7..55a851788 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -1,4 +1,3 @@ - import { GC, splitItem, diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 5b93369ff..c5931ab44 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -1,4 +1,3 @@ - import { getState, writeStructsFromTransaction, diff --git a/src/utils/UpdateEncoder.js b/src/utils/UpdateEncoder.js index e8c5d06c8..8cf303819 100644 --- a/src/utils/UpdateEncoder.js +++ b/src/utils/UpdateEncoder.js @@ -1,4 +1,3 @@ - import * as error from 'lib0/error' import * as encoding from 'lib0/encoding' diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index b47d8c9ad..131741488 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -1,4 +1,3 @@ - import { isDeleted, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line diff --git a/src/utils/encoding.js b/src/utils/encoding.js index a86f7b427..2277a1407 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -1,4 +1,3 @@ - /** * @module encoding */ diff --git a/src/utils/isParentOf.js b/src/utils/isParentOf.js index d3012e24d..d8f5a613b 100644 --- a/src/utils/isParentOf.js +++ b/src/utils/isParentOf.js @@ -1,4 +1,3 @@ - import { AbstractType, Item } from '../internals.js' // eslint-disable-line /** diff --git a/src/utils/logging.js b/src/utils/logging.js index 377097638..989ac4881 100644 --- a/src/utils/logging.js +++ b/src/utils/logging.js @@ -1,4 +1,3 @@ - import { AbstractType // eslint-disable-line } from '../internals.js' diff --git a/src/utils/updates.js b/src/utils/updates.js index c64ce355e..fc40cd572 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -1,4 +1,3 @@ - import * as binary from 'lib0/binary' import * as decoding from 'lib0/decoding' import * as encoding from 'lib0/encoding' diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 3a7ad4fe0..fc71364c2 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -1,4 +1,3 @@ - /** * Testing if encoding/decoding compatibility and integration compatiblity is given. * We expect that the document always looks the same, even if we upgrade the integration algorithm, or add additional encoding approaches. diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 605249360..bb94819d1 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -1,4 +1,3 @@ - import * as Y from '../src/index.js' import * as t from 'lib0/testing' diff --git a/tests/relativePositions.tests.js b/tests/relativePositions.tests.js index 817b98052..93fec2308 100644 --- a/tests/relativePositions.tests.js +++ b/tests/relativePositions.tests.js @@ -1,4 +1,3 @@ - import * as Y from '../src/index.js' import * as t from 'lib0/testing' diff --git a/tests/testHelper.js b/tests/testHelper.js index f1ff47561..c74b7438e 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -1,4 +1,3 @@ - import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as encoding from 'lib0/encoding' diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 1844af86b..f1dbf4287 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -3,6 +3,46 @@ import { init } from './testHelper.js' // eslint-disable-line import * as Y from '../src/index.js' import * as t from 'lib0/testing' +export const testInconsistentFormat = () => { + /** + * @param {Y.Doc} ydoc + */ + const testYjsMerge = ydoc => { + const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText)) + content.format(0, 6, { bold: null }) + content.format(6, 4, { type: 'text' }) + t.compare(content.toDelta(), [ + { + attributes: { type: 'text' }, + insert: 'Merge Test' + }, + { + attributes: { type: 'text', italic: true }, + insert: ' After' + } + ]) + } + const initializeYDoc = () => { + const yDoc = new Y.Doc({ gc: false }) + + const content = /** @type {Y.XmlText} */ (yDoc.get('text', Y.XmlText)) + content.insert(0, ' After', { type: 'text', italic: true }) + content.insert(0, 'Test', { type: 'text' }) + content.insert(0, 'Merge ', { type: 'text', bold: true }) + return yDoc + } + { + const yDoc = initializeYDoc() + testYjsMerge(yDoc) + } + { + const initialYDoc = initializeYDoc() + const yDoc = new Y.Doc({ gc: false }) + Y.applyUpdate(yDoc, Y.encodeStateAsUpdate(initialYDoc)) + testYjsMerge(yDoc) + } +} + /** * @param {t.TestCase} tc */ From 415a645874bd7757d98d5a7d4461cb35e29ebbde Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 21 Jan 2024 11:30:14 +0100 Subject: [PATCH 116/362] 13.6.11 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 41934864d..e07ca40b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.10", + "version": "13.6.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.10", + "version": "13.6.11", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index bdefe3206..9bdb1055b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.10", + "version": "13.6.11", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 16d9638bc80384b833101eb06d51c1fd6016934f Mon Sep 17 00:00:00 2001 From: Raine Revere Date: Mon, 5 Feb 2024 13:24:11 +0000 Subject: [PATCH 117/362] Add ydoc.getXmlElement --- README.md | 2 ++ src/utils/Doc.js | 12 ++++++++++++ tests/y-xml.tests.js | 13 +++++++++++++ 3 files changed, 27 insertions(+) diff --git a/README.md b/README.md index 6f85686b1..a8ebef99b 100644 --- a/README.md +++ b/README.md @@ -739,6 +739,8 @@ type. Doesn't log types that have not been defined (using
Define a shared Y.Map type. Is equivalent to y.get(string, Y.Map).
getText(string):Y.Text
Define a shared Y.Text type. Is equivalent to y.get(string, Y.Text).
+ getXmlElement(string, string):Y.XmlElement +
Define a shared Y.XmlElement type. Is equivalent to y.get(string, Y.XmlElement).
getXmlFragment(string):Y.XmlFragment
Define a shared Y.XmlFragment type. Is equivalent to y.get(string, Y.XmlFragment).
on(string, function) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index ddde1d543..e809e7ecf 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -8,6 +8,7 @@ import { YArray, YText, YMap, + YXmlElement, YXmlFragment, transact, ContentDoc, Item, Transaction, YEvent // eslint-disable-line @@ -262,6 +263,17 @@ export class Doc extends Observable { return this.get(name, YMap) } + /** + * @param {string} [name] + * @return {YXmlElement} + * + * @public + */ + getXmlElement (name = '') { + // @ts-ignore + return this.get(name, YXmlElement) + } + /** * @param {string} [name] * @return {YXmlFragment} diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 1ae2c8139..22f12be91 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -210,3 +210,16 @@ export const testFormattingBug = _tc => { yxml.applyDelta(delta) t.compare(yxml.toDelta(), delta) } + +/** + * @param {t.TestCase} _tc + */ +export const testElement = _tc => { + const ydoc = new Y.Doc() + const yxmlel = ydoc.getXmlElement() + + const text1 = new Y.XmlText('text1') + const text2 = new Y.XmlText('text2') + yxmlel.insert(0, [text1, text2]) + t.compareArrays(yxmlel.toArray(), [text1, text2]) +} From e1bce03ed8d37898e6e7289d49e1e56da9ce821a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 9 Feb 2024 23:27:24 +0100 Subject: [PATCH 118/362] better typings for ydoc.get --- src/types/YXmlElement.js | 2 +- src/utils/Doc.js | 22 +++++++++------------- tests/y-xml.tests.js | 2 -- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 7b18be694..1c1f3190e 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -20,7 +20,7 @@ import { /** * An YXmlElement imitates the behavior of a - * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}. + * https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element * * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement diff --git a/src/utils/Doc.js b/src/utils/Doc.js index e809e7ecf..0d8d471b2 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -181,6 +181,7 @@ export class Doc extends Observable { * Define all types right after the Yjs instance is created and store them in a separate object. * Also use the typed methods `getText(name)`, `getArray(name)`, .. * + * @template {typeof AbstractType} Type * @example * const y = new Y(..) * const appState = { @@ -189,12 +190,12 @@ export class Doc extends Observable { * } * * @param {string} name - * @param {Function} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ... - * @return {AbstractType} The created type. Constructed with TypeConstructor + * @param {Type} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ... + * @return {InstanceType} The created type. Constructed with TypeConstructor * * @public */ - get (name, TypeConstructor = AbstractType) { + get (name, TypeConstructor = /** @type {any} */ (AbstractType)) { const type = map.setIfUndefined(this.share, name, () => { // @ts-ignore const t = new TypeConstructor() @@ -220,12 +221,12 @@ export class Doc extends Observable { t._length = type._length this.share.set(name, t) t._integrate(this, null) - return t + return /** @type {InstanceType} */ (t) } else { throw new Error(`Type with the name ${name} has already been defined with a different constructor`) } } - return type + return /** @type {InstanceType} */ (type) } /** @@ -236,8 +237,7 @@ export class Doc extends Observable { * @public */ getArray (name = '') { - // @ts-ignore - return this.get(name, YArray) + return /** @type {YArray} */ (this.get(name, YArray)) } /** @@ -247,7 +247,6 @@ export class Doc extends Observable { * @public */ getText (name = '') { - // @ts-ignore return this.get(name, YText) } @@ -259,8 +258,7 @@ export class Doc extends Observable { * @public */ getMap (name = '') { - // @ts-ignore - return this.get(name, YMap) + return /** @type {YMap} */ (this.get(name, YMap)) } /** @@ -270,8 +268,7 @@ export class Doc extends Observable { * @public */ getXmlElement (name = '') { - // @ts-ignore - return this.get(name, YXmlElement) + return /** @type {YXmlElement<{[key:string]:string}>} */ (this.get(name, YXmlElement)) } /** @@ -281,7 +278,6 @@ export class Doc extends Observable { * @public */ getXmlFragment (name = '') { - // @ts-ignore return this.get(name, YXmlFragment) } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 22f12be91..a9395c2d6 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -189,7 +189,6 @@ export const testClone = _tc => { const third = new Y.XmlElement('p') yxml.push([first, second, third]) t.compareArrays(yxml.toArray(), [first, second, third]) - const cloneYxml = yxml.clone() ydoc.getArray('copyarr').insert(0, [cloneYxml]) t.assert(cloneYxml.length === 3) @@ -217,7 +216,6 @@ export const testFormattingBug = _tc => { export const testElement = _tc => { const ydoc = new Y.Doc() const yxmlel = ydoc.getXmlElement() - const text1 = new Y.XmlText('text1') const text2 = new Y.XmlText('text2') yxmlel.insert(0, [text1, text2]) From ce06b2abec864e56628b3f408a4a274681a3321a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 9 Feb 2024 23:31:07 +0100 Subject: [PATCH 119/362] update deps --- package-lock.json | 1121 ++++++++++++++++++++++++++++++--------------- 1 file changed, 742 insertions(+), 379 deletions(-) diff --git a/package-lock.json b/package-lock.json index e07ca40b9..aefd16a0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,35 +34,45 @@ "url": "https://github.com/sponsors/dmonad" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -70,9 +80,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -102,6 +112,16 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -128,6 +148,18 @@ "node": ">= 4" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/eslintrc/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -135,15 +167,15 @@ "dev": true }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@rollup/plugin-commonjs": { - "version": "24.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz", - "integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==", + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", + "integrity": "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -165,56 +197,16 @@ } } }, - "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", - "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==", + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.0", + "is-builtin-module": "^3.2.1", "is-module": "^1.0.0", "resolve": "^1.22.1" }, @@ -222,7 +214,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^2.78.0||^3.0.0" + "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -231,9 +223,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", "dev": true, "dependencies": { "@types/estree": "^1.0.0", @@ -244,7 +236,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -253,9 +245,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", - "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "node_modules/@types/json5": { @@ -265,9 +257,9 @@ "dev": true }, "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", + "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", "dev": true }, "node_modules/@types/markdown-it": { @@ -281,16 +273,19 @@ } }, "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", + "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", "dev": true }, "node_modules/@types/node": { - "version": "18.15.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz", - "integrity": "sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==", - "dev": true + "version": "18.19.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.15.tgz", + "integrity": "sha512-AMZ2UWx+woHNfM11PyAEQmfSxi05jm9OlkxczuHeEqmvwPkYj6MWv44gbzDPefYOLysTOFyI3ziiy2ONmUZfpA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/resolve": { "version": "1.20.2", @@ -372,28 +367,31 @@ "dev": true }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "is-string": "^1.0.7" }, "engines": { @@ -404,14 +402,14 @@ } }, "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -422,14 +420,14 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", "es-shim-unscopables": "^1.0.0" }, "engines": { @@ -439,6 +437,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -458,9 +478,9 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", + "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", "dev": true, "engines": { "node": ">= 0.4" @@ -497,13 +517,12 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/builtin-modules": { @@ -519,13 +538,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", + "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "set-function-length": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -773,12 +797,28 @@ "node": ">=0.10.0" } }, + "node_modules/define-data-property": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", + "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -865,12 +905,13 @@ "dev": true }, "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8.6" @@ -895,25 +936,26 @@ } }, "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -921,19 +963,23 @@ "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -942,27 +988,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -1096,20 +1151,20 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -1171,6 +1226,16 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1192,6 +1257,18 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-import/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1218,10 +1295,32 @@ "eslint": ">=5.16.0" } }, + "node_modules/eslint-plugin-node/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -1266,6 +1365,16 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7" } }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -1278,13 +1387,25 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -1365,6 +1486,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1434,6 +1565,18 @@ "node": ">= 4" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1441,9 +1584,9 @@ "dev": true }, "node_modules/eslint/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1600,12 +1743,13 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -1613,15 +1757,15 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true, "funding": [ { @@ -1654,9 +1798,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -1668,21 +1812,24 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -1707,14 +1854,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1730,13 +1882,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -1746,20 +1899,19 @@ } }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1832,13 +1984,10 @@ "dev": true }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, "engines": { "node": ">= 0.4.0" } @@ -1862,12 +2011,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1898,12 +2047,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -1912,6 +2061,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -2043,13 +2204,13 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -2057,14 +2218,16 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2132,12 +2295,12 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2289,16 +2452,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -2319,6 +2478,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2409,6 +2574,12 @@ "node": ">=8" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -2446,18 +2617,29 @@ "dev": true }, "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { "node": ">=4.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/klaw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", @@ -2481,9 +2663,9 @@ } }, "node_modules/lib0": { - "version": "0.2.86", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.86.tgz", - "integrity": "sha512-kxigQTM4Q7NwJkEgdqQvU21qiR37twcqqLmh+/SbiGbRLfPlLVbHyY9sWp7PwXh0Xus9ELDSjsUOwcrdt5yZ4w==", + "version": "0.2.88", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.88.tgz", + "integrity": "sha512-KyroiEvCeZcZEMx5Ys+b4u4eEBbA1ch7XUaBhYpwa/nPMrzTjUhI4RfcytmQfYoTBPcdyx+FX6WFNIoNuJzJfQ==", "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -2728,6 +2910,16 @@ "node": ">=10" } }, + "node_modules/markdownlint-cli/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/markdownlint-cli/node_modules/commander": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", @@ -2819,9 +3011,9 @@ } }, "node_modules/marked": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", - "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -2849,15 +3041,15 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/minimist": { @@ -2924,9 +3116,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2942,13 +3134,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -2960,28 +3152,28 @@ } }, "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -2991,27 +3183,27 @@ } }, "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -3039,17 +3231,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -3355,18 +3547,18 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", - "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", "dev": true, "dependencies": { "side-channel": "^1.0.4" @@ -3459,14 +3651,14 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -3512,12 +3704,12 @@ } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -3552,10 +3744,52 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/rollup": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.0.tgz", - "integrity": "sha512-YsIfrk80NqUDrxrjWPXUa7PWvAfegZEXHuPsEZg58fGCdjL1I9C1i/NaG+L+27kxxwkrG/QEDEQc8s/ynXWWGQ==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -3574,6 +3808,24 @@ "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", "dev": true }, + "node_modules/safe-array-concat": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", + "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3595,15 +3847,18 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3615,14 +3870,45 @@ "dev": true }, "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" } }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3645,14 +3931,18 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3725,9 +4015,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", + "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -3741,9 +4031,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", + "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", "dev": true }, "node_modules/sprintf-js": { @@ -3853,18 +4143,19 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", "side-channel": "^1.0.4" }, "funding": { @@ -3872,14 +4163,14 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { "node": ">= 0.4" @@ -3889,28 +4180,28 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4033,9 +4324,9 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", @@ -4074,6 +4365,57 @@ "node": ">=8" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", + "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -4128,6 +4470,12 @@ "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -4162,9 +4510,9 @@ "dev": true }, "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, "node_modules/validate-npm-package-license": { @@ -4209,17 +4557,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", + "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.6", + "call-bind": "^1.0.5", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -4228,15 +4575,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4259,16 +4597,23 @@ "dev": true }, "node_modules/y-protocols": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.5.tgz", - "integrity": "sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", + "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", "dev": true, "dependencies": { - "lib0": "^0.2.42" + "lib0": "^0.2.85" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" }, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^13.0.0" } }, "node_modules/yallist": { @@ -4276,6 +4621,24 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true + }, + "node_modules/yjs": { + "version": "13.6.11", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.11.tgz", + "integrity": "sha512-FvRRJKX9u270dOLkllGF/UDCWwmIv2Z+ucM4v1QO1TuxdmoiMnSUXH1HAcOKOrkBEhQtPTkxep7tD2DrQB+l0g==", + "dev": true, + "peer": true, + "dependencies": { + "lib0": "^0.2.86" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } } } } From a8582442e31de4f185fc04b13935ceeb31354126 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 9 Feb 2024 23:38:50 +0100 Subject: [PATCH 120/362] 13.6.12 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index aefd16a0f..e11d6c60c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.11", + "version": "13.6.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.11", + "version": "13.6.12", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 9bdb1055b..d0133c497 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.11", + "version": "13.6.12", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 009f6ab5510acf502e2459bef7852c61ad8cec34 Mon Sep 17 00:00:00 2001 From: MentalGear <2837147+MentalGear@users.noreply.github.com> Date: Sat, 17 Feb 2024 20:38:53 +0100 Subject: [PATCH 121/362] docs: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8ebef99b..97d08cd5d 100644 --- a/README.md +++ b/README.md @@ -878,7 +878,7 @@ ydoc2.getText().toString() // => "00000000000" #### Using V2 update format Yjs implements two update formats. By default you are using the V1 update format. -You can opt-in into the V2 update format wich provides much better compression. +You can opt-in into the V2 update format which provides much better compression. It is not yet used by all providers. However, you can already use it if you are building your own provider. All below functions are available with the suffix "V2". E.g. `Y.applyUpdate` ⇒ `Y.applyUpdateV2`. Also when listening to updates From 90a90ab010665216401543648ee5d0747b71b64e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 20 Feb 2024 19:52:58 +0100 Subject: [PATCH 122/362] add y-fire to provider list #189 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a8ebef99b..df66419a4 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,10 @@ y-websocket provider.
Like y-indexeddb, but with sub-documents support and fully TypeScript.
+
y-fire
+
+A database and connection provider for Yjs based on Firestore. +
# Ports From 917261a1cedde3e762f0890f8132f9c31547d5b3 Mon Sep 17 00:00:00 2001 From: Myles J Date: Wed, 28 Feb 2024 23:58:37 +0000 Subject: [PATCH 123/362] Facilitate referencing UndoManager StackItem inside Type observers --- src/utils/UndoManager.js | 10 +++++++ tests/undo-redo.tests.js | 63 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index f6f13ee58..c0dca63d6 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -191,6 +191,12 @@ export class UndoManager extends Observable { */ this.undoing = false this.redoing = false + /** + * The currently popped stack item if UndoManager.undoing or UndoManager.redoing + * + * @type {StackItem|null} + */ + this.doingStackItem = null this.lastChange = 0 this.ignoreRemoteMapChanges = ignoreRemoteMapChanges this.captureTimeout = captureTimeout @@ -331,10 +337,12 @@ export class UndoManager extends Observable { */ undo () { this.undoing = true + this.doingStackItem = array.last(this.undoStack) ?? null let res try { res = popStackItem(this, this.undoStack, 'undo') } finally { + this.doingStackItem = null this.undoing = false } return res @@ -347,10 +355,12 @@ export class UndoManager extends Observable { */ redo () { this.redoing = true + this.doingStackItem = array.last(this.redoStack) ?? null let res try { res = popStackItem(this, this.redoStack, 'redo') } finally { + this.doingStackItem = null this.redoing = false } return res diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index f1dbf4287..82c981297 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -715,3 +715,66 @@ export const testUndoDeleteInMap = (tc) => { undoManager.undo() t.compare(map0.toJSON(), { a: 'a' }) } + +/** + * It should expose the StackItem being processed if undoing + * + * @param {t.TestCase} tc + */ +export const testUndoDoingStackItem = async (tc) => { + const doc = new Y.Doc() + const text = doc.getText('text') + const undoManager = new Y.UndoManager([text]) + + undoManager.on('stack-item-added', /** @param {any} event */ event => { + event.stackItem.meta.set('str', '42') + }) + + const meta = new Promise((resolve) => { + setTimeout(() => resolve('ABORTED'), 50) + text.observe((event) => { + const /** @type {Y.UndoManager} */ origin = event.transaction.origin + if (origin === undoManager && origin.undoing) { + resolve(origin.doingStackItem?.meta.get('str')) + } + }) + }) + + text.insert(0, 'abc') + undoManager.undo() + + t.compare(await meta, '42') + t.compare(undoManager.doingStackItem, null) +} + +/** + * It should expose the StackItem being processed if redoing + * + * @param {t.TestCase} tc + */ +export const testRedoDoingStackItem = async (tc) => { + const doc = new Y.Doc() + const text = doc.getText('text') + const undoManager = new Y.UndoManager([text]) + + undoManager.on('stack-item-added', /** @param {any} event */ event => { + event.stackItem.meta.set('str', '42') + }) + + const meta = new Promise(resolve => { + setTimeout(() => resolve('ABORTED'), 50) + text.observe((event) => { + const /** @type {Y.UndoManager} */ origin = event.transaction.origin + if (origin === undoManager && origin.redoing) { + resolve(origin.doingStackItem?.meta.get('str')) + } + }) + }) + + text.insert(0, 'abc') + undoManager.undo() + undoManager.redo() + + t.compare(await meta, '42') + t.compare(undoManager.doingStackItem, null) +} From 29fa60ccf9fd537352de088fdf362e7df25fa076 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 29 Feb 2024 14:46:43 +0100 Subject: [PATCH 124/362] [Undo] add UndoManager.currStackItem --- package.json | 3 +- src/utils/UndoManager.js | 22 +++++---------- tests/undo-redo.tests.js | 61 +++++++++------------------------------- 3 files changed, 23 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index d0133c497..165444931 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,10 @@ "url": "https://github.com/sponsors/dmonad" }, "scripts": { + "clean": "rm -rf dist docs", "test": "npm run dist && node ./dist/tests.cjs --repetition-time 50", "test-extensive": "npm run lint && npm run dist && node ./dist/tests.cjs --production --repetition-time 10000", - "dist": "rm -rf dist && rollup -c && tsc", + "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", diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index c0dca63d6..16ccb7ae5 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -52,11 +52,6 @@ const clearUndoManagerStackItem = (tr, um, stackItem) => { * @return {StackItem?} */ const popStackItem = (undoManager, stack, eventType) => { - /** - * Whether a change happened - * @type {StackItem?} - */ - let result = null /** * Keep a reference to the transaction so we can fire the event with the changedParentTypes * @type {any} @@ -65,7 +60,7 @@ const popStackItem = (undoManager, stack, eventType) => { const doc = undoManager.doc const scope = undoManager.scope transact(doc, transaction => { - while (stack.length > 0 && result === null) { + while (stack.length > 0 && undoManager.currStackItem === null) { const store = doc.store const stackItem = /** @type {StackItem} */ (stack.pop()) /** @@ -113,7 +108,7 @@ const popStackItem = (undoManager, stack, eventType) => { performedChange = true } } - result = performedChange ? stackItem : null + undoManager.currStackItem = performedChange ? stackItem : null } transaction.changed.forEach((subProps, type) => { // destroy search marker if necessary @@ -123,11 +118,12 @@ const popStackItem = (undoManager, stack, eventType) => { }) _tr = transaction }, undoManager) - if (result != null) { + if (undoManager.currStackItem != null) { const changedParentTypes = _tr.changedParentTypes - undoManager.emit('stack-item-popped', [{ stackItem: result, type: eventType, changedParentTypes }, undoManager]) + undoManager.emit('stack-item-popped', [{ stackItem: undoManager.currStackItem, type: eventType, changedParentTypes }, undoManager]) + undoManager.currStackItem = null } - return result + return undoManager.currStackItem } /** @@ -196,7 +192,7 @@ export class UndoManager extends Observable { * * @type {StackItem|null} */ - this.doingStackItem = null + this.currStackItem = null this.lastChange = 0 this.ignoreRemoteMapChanges = ignoreRemoteMapChanges this.captureTimeout = captureTimeout @@ -337,12 +333,10 @@ export class UndoManager extends Observable { */ undo () { this.undoing = true - this.doingStackItem = array.last(this.undoStack) ?? null let res try { res = popStackItem(this, this.undoStack, 'undo') } finally { - this.doingStackItem = null this.undoing = false } return res @@ -355,12 +349,10 @@ export class UndoManager extends Observable { */ redo () { this.redoing = true - this.doingStackItem = array.last(this.redoStack) ?? null let res try { res = popStackItem(this, this.redoStack, 'redo') } finally { - this.doingStackItem = null this.redoing = false } return res diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 82c981297..2fb2ff2e2 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -719,62 +719,29 @@ export const testUndoDeleteInMap = (tc) => { /** * It should expose the StackItem being processed if undoing * - * @param {t.TestCase} tc - */ -export const testUndoDoingStackItem = async (tc) => { - const doc = new Y.Doc() - const text = doc.getText('text') - const undoManager = new Y.UndoManager([text]) - - undoManager.on('stack-item-added', /** @param {any} event */ event => { - event.stackItem.meta.set('str', '42') - }) - - const meta = new Promise((resolve) => { - setTimeout(() => resolve('ABORTED'), 50) - text.observe((event) => { - const /** @type {Y.UndoManager} */ origin = event.transaction.origin - if (origin === undoManager && origin.undoing) { - resolve(origin.doingStackItem?.meta.get('str')) - } - }) - }) - - text.insert(0, 'abc') - undoManager.undo() - - t.compare(await meta, '42') - t.compare(undoManager.doingStackItem, null) -} - -/** - * It should expose the StackItem being processed if redoing - * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testRedoDoingStackItem = async (tc) => { +export const testUndoDoingStackItem = async (_tc) => { const doc = new Y.Doc() const text = doc.getText('text') const undoManager = new Y.UndoManager([text]) - undoManager.on('stack-item-added', /** @param {any} event */ event => { event.stackItem.meta.set('str', '42') }) - - const meta = new Promise(resolve => { - setTimeout(() => resolve('ABORTED'), 50) - text.observe((event) => { - const /** @type {Y.UndoManager} */ origin = event.transaction.origin - if (origin === undoManager && origin.redoing) { - resolve(origin.doingStackItem?.meta.get('str')) - } - }) + let metaUndo = /** @type {any} */ (null) + let metaRedo = /** @type {any} */ (null) + text.observe((event) => { + const /** @type {Y.UndoManager} */ origin = event.transaction.origin + if (origin === undoManager && origin.undoing) { + metaUndo = origin.currStackItem?.meta.get('str') + } else if (origin === undoManager && origin.redoing) { + metaRedo = origin.currStackItem?.meta.get('str') + } }) - text.insert(0, 'abc') undoManager.undo() undoManager.redo() - - t.compare(await meta, '42') - t.compare(undoManager.doingStackItem, null) + t.compare(metaUndo, '42', 'currStackItem is accessible while undoing') + t.compare(metaRedo, '42', 'currStackItem is accessible while redoing') + t.compare(undoManager.currStackItem, null, 'currStackItem is null after observe/transaction') } From 541306b254dac825a8e8f2393936bb67975f0122 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 29 Feb 2024 17:08:57 +0100 Subject: [PATCH 125/362] migrate to ObservableV2 --- src/utils/AbstractConnector.js | 6 ++--- src/utils/Doc.js | 43 +++++++++++++++++----------------- src/utils/UndoManager.js | 23 +++++++++++++----- tests/testHelper.js | 4 ++-- tests/updates.tests.js | 16 ++++++------- 5 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/utils/AbstractConnector.js b/src/utils/AbstractConnector.js index 5f5446844..f5c0566a6 100644 --- a/src/utils/AbstractConnector.js +++ b/src/utils/AbstractConnector.js @@ -1,4 +1,4 @@ -import { Observable } from 'lib0/observable' +import { ObservableV2 } from 'lib0/observable' import { Doc // eslint-disable-line @@ -10,9 +10,9 @@ import { * @note This interface is experimental and it is not advised to actually inherit this class. * It just serves as typing information. * - * @extends {Observable} + * @extends {ObservableV2} */ -export class AbstractConnector extends Observable { +export class AbstractConnector extends ObservableV2 { /** * @param {Doc} ydoc * @param {any} awareness diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 0d8d471b2..8a8936b9d 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -14,7 +14,7 @@ import { ContentDoc, Item, Transaction, YEvent // eslint-disable-line } from '../internals.js' -import { Observable } from 'lib0/observable' +import { ObservableV2 } from 'lib0/observable' import * as random from 'lib0/random' import * as map from 'lib0/map' import * as array from 'lib0/array' @@ -33,11 +33,27 @@ export const generateNewClientId = random.uint32 * @property {boolean} [DocOpts.shouldLoad] Whether the document should be synced by the provider now. This is toggled to true when you call ydoc.load() */ +/** + * @typedef {Object} DocEvents + * @property {function(Doc):void} DocEvents.destroy + * @property {function(Doc):void} DocEvents.load + * @property {function(boolean, Doc):void} DocEvents.sync + * @property {function(Uint8Array, any, Doc, Transaction):void} DocEvents.update + * @property {function(Uint8Array, any, Doc, Transaction):void} DocEvents.updateV2 + * @property {function(Doc):void} DocEvents.beforeAllTransactions + * @property {function(Transaction, Doc):void} DocEvents.beforeTransaction + * @property {function(Transaction, Doc):void} DocEvents.beforeObserverCalls + * @property {function(Transaction, Doc):void} DocEvents.afterTransaction + * @property {function(Transaction, Doc):void} DocEvents.afterTransactionCleanup + * @property {function(Doc, Array):void} DocEvents.afterAllTransactions + * @property {function({ loaded: Set, added: Set, removed: Set }, Doc, Transaction):void} DocEvents.subdocs + */ + /** * A Yjs instance handles the state of shared data. - * @extends Observable + * @extends ObservableV2 */ -export class Doc extends Observable { +export class Doc extends ObservableV2 { /** * @param {DocOpts} opts configuration */ @@ -115,7 +131,7 @@ export class Doc extends Observable { } this.isSynced = isSynced === undefined || isSynced === true if (this.isSynced && !this.isLoaded) { - this.emit('load', []) + this.emit('load', [this]) } }) /** @@ -321,24 +337,9 @@ export class Doc extends Observable { transaction.subdocsRemoved.add(this) }, null, true) } - this.emit('destroyed', [true]) + // @ts-ignore + this.emit('destroyed', [true]) // DEPRECATED! this.emit('destroy', [this]) super.destroy() } - - /** - * @param {string} eventName - * @param {function(...any):any} f - */ - on (eventName, f) { - super.on(eventName, f) - } - - /** - * @param {string} eventName - * @param {function} f - */ - off (eventName, f) { - super.off(eventName, f) - } } diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 16ccb7ae5..27023ecfa 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -10,13 +10,13 @@ import { getItemCleanStart, isDeleted, addToDeleteSet, - Transaction, Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line + YEvent, Transaction, Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line } from '../internals.js' import * as time from 'lib0/time' import * as array from 'lib0/array' import * as logging from 'lib0/logging' -import { Observable } from 'lib0/observable' +import { ObservableV2 } from 'lib0/observable' export class StackItem { /** @@ -48,7 +48,7 @@ const clearUndoManagerStackItem = (tr, um, stackItem) => { /** * @param {UndoManager} undoManager * @param {Array} stack - * @param {string} eventType + * @param {'undo'|'redo'} eventType * @return {StackItem?} */ const popStackItem = (undoManager, stack, eventType) => { @@ -120,7 +120,7 @@ const popStackItem = (undoManager, stack, eventType) => { }, undoManager) if (undoManager.currStackItem != null) { const changedParentTypes = _tr.changedParentTypes - undoManager.emit('stack-item-popped', [{ stackItem: undoManager.currStackItem, type: eventType, changedParentTypes }, undoManager]) + undoManager.emit('stack-item-popped', [{ stackItem: undoManager.currStackItem, type: eventType, changedParentTypes, origin: undoManager }, undoManager]) undoManager.currStackItem = null } return undoManager.currStackItem @@ -139,6 +139,14 @@ const popStackItem = (undoManager, stack, eventType) => { * @property {Doc} [doc] The document that this UndoManager operates on. Only needed if typeScope is empty. */ +/** + * @typedef {Object} StackItemEvent + * @property {StackItem} StackItemEvent.stackItem + * @property {any} StackItemEvent.origin + * @property {'undo'|'redo'} StackItemEvent.type + * @property {Map>,Array>>} StackItemEvent.changedParentTypes + */ + /** * Fires 'stack-item-added' event when a stack item was added to either the undo- or * the redo-stack. You may store additional stack information via the @@ -146,9 +154,9 @@ const popStackItem = (undoManager, stack, eventType) => { * Fires 'stack-item-popped' event when a stack item was popped from either the * undo- or the redo-stack. You may restore the saved stack information from `event.stackItem.meta`. * - * @extends {Observable<'stack-item-added'|'stack-item-popped'|'stack-cleared'|'stack-item-updated'>} + * @extends {ObservableV2<{'stack-item-added':function(StackItemEvent, UndoManager):void, 'stack-item-popped': function(StackItemEvent, UndoManager):void, 'stack-cleared': function({ undoStackCleared: boolean, redoStackCleared: boolean }):void, 'stack-item-updated': function(StackItemEvent, UndoManager):void }>} */ -export class UndoManager extends Observable { +export class UndoManager extends ObservableV2 { /** * @param {AbstractType|Array>} typeScope Accepts either a single type, or an array of types * @param {UndoManagerOptions} options @@ -246,6 +254,9 @@ export class UndoManager extends Observable { keepItem(item, true) } }) + /** + * @type {[StackItemEvent, UndoManager]} + */ const changeEvent = [{ stackItem: stack[stack.length - 1], origin: transaction.origin, type: undoing ? 'redo' : 'undo', changedParentTypes: transaction.changedParentTypes }, this] if (didAdd) { this.emit('stack-item-added', changeEvent) diff --git a/tests/testHelper.js b/tests/testHelper.js index c74b7438e..569836794 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -34,7 +34,7 @@ export const encV1 = { mergeUpdates: Y.mergeUpdates, applyUpdate: Y.applyUpdate, logUpdate: Y.logUpdate, - updateEventName: 'update', + updateEventName: /** @type {'update'} */ ('update'), diffUpdate: Y.diffUpdate } @@ -43,7 +43,7 @@ export const encV2 = { mergeUpdates: Y.mergeUpdatesV2, applyUpdate: Y.applyUpdateV2, logUpdate: Y.logUpdateV2, - updateEventName: 'updateV2', + updateEventName: /** @type {'updateV2'} */ ('updateV2'), diffUpdate: Y.diffUpdateV2 } diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 4ba3bab62..f3169cfee 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -15,7 +15,7 @@ import * as object from 'lib0/object' * @property {function(Uint8Array):{from:Map,to:Map}} Enc.parseUpdateMeta * @property {function(Y.Doc):Uint8Array} Enc.encodeStateVector * @property {function(Uint8Array):Uint8Array} Enc.encodeStateVectorFromUpdate - * @property {string} Enc.updateEventName + * @property {'update'|'updateV2'} Enc.updateEventName * @property {string} Enc.description * @property {function(Uint8Array, Uint8Array):Uint8Array} Enc.diffUpdate */ @@ -169,7 +169,7 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { // t.info('Target State: ') // enc.logUpdate(targetState) - cases.forEach((mergedUpdates, i) => { + cases.forEach((mergedUpdates) => { // t.info('State Case $' + i + ':') // enc.logUpdate(updates) const merged = new Y.Doc({ gc: false }) @@ -218,10 +218,10 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testMergeUpdates1 = tc => { - encoders.forEach((enc, i) => { +export const testMergeUpdates1 = _tc => { + encoders.forEach((enc) => { t.info(`Using encoder: ${enc.description}`) const ydoc = new Y.Doc({ gc: false }) const updates = /** @type {Array} */ ([]) @@ -299,16 +299,16 @@ export const testMergePendingUpdates = tc => { Y.applyUpdate(yDoc5, update4) Y.applyUpdate(yDoc5, serverUpdates[4]) // @ts-ignore - const update5 = Y.encodeStateAsUpdate(yDoc5) // eslint-disable-line + const _update5 = Y.encodeStateAsUpdate(yDoc5) // eslint-disable-line const yText5 = yDoc5.getText('textBlock') t.compareStrings(yText5.toString(), 'nenor') } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testObfuscateUpdates = tc => { +export const testObfuscateUpdates = _tc => { const ydoc = new Y.Doc() const ytext = ydoc.getText('text') const ymap = ydoc.getMap('map') From 90675be3ab74df86cfd2e15d49f1f7efb00743f2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 29 Feb 2024 17:37:25 +0100 Subject: [PATCH 126/362] 13.6.13 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e11d6c60c..0f59a9202 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.12", + "version": "13.6.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.12", + "version": "13.6.13", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 165444931..43dee697e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.12", + "version": "13.6.13", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From cdbb55818dab520fa09987e19d82766e7dddfad9 Mon Sep 17 00:00:00 2001 From: Julian Lehrhuber Date: Fri, 1 Mar 2024 10:37:51 +0100 Subject: [PATCH 127/362] Allow falsy attribute values --- src/types/YText.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/YText.js b/src/types/YText.js index c02ae6e56..50a214906 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -227,7 +227,7 @@ const insertAttributes = (transaction, parent, currPos, attributes) => { // insert format-start items for (const key in attributes) { const val = attributes[key] - const currentVal = currPos.currentAttributes.get(key) || null + const currentVal = currPos.currentAttributes.get(key) ?? null if (!equalAttrs(currentVal, val)) { // save negated attribute (set null if currentVal undefined) negatedAttributes.set(key, currentVal) From 133cfc9cdc91517b1d9777384effc4a0fbb8fade Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 1 Mar 2024 11:29:14 +0100 Subject: [PATCH 128/362] allow falsy values in formatting attributes --- src/types/YText.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 50a214906..8de9260e8 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -201,7 +201,7 @@ const minimizeAttributeChanges = (currPos, attributes) => { while (true) { if (currPos.right === null) { break - } else if (currPos.right.deleted || (currPos.right.content.constructor === ContentFormat && equalAttrs(attributes[(/** @type {ContentFormat} */ (currPos.right.content)).key] || null, /** @type {ContentFormat} */ (currPos.right.content).value))) { + } else if (currPos.right.deleted || (currPos.right.content.constructor === ContentFormat && equalAttrs(attributes[(/** @type {ContentFormat} */ (currPos.right.content)).key] ?? null, /** @type {ContentFormat} */ (currPos.right.content).value))) { // } else { break @@ -389,12 +389,12 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt switch (content.constructor) { case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (content) - const startAttrValue = startAttributes.get(key) || null + const startAttrValue = startAttributes.get(key) ?? null if (endFormats.get(key) !== content || startAttrValue === value) { // Either this format is overwritten or it is not necessary because the attribute already existed. start.delete(transaction) cleanups++ - if (!reachedCurr && (currAttributes.get(key) || null) === value && startAttrValue !== value) { + if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) { if (startAttrValue === null) { currAttributes.delete(key) } else { @@ -769,12 +769,12 @@ export class YTextEvent extends YEvent { const { key, value } = /** @type {ContentFormat} */ (item.content) if (this.adds(item)) { if (!this.deletes(item)) { - const curVal = currentAttributes.get(key) || null + const curVal = currentAttributes.get(key) ?? null if (!equalAttrs(curVal, value)) { if (action === 'retain') { addOp() } - if (equalAttrs(value, (oldAttributes.get(key) || null))) { + if (equalAttrs(value, (oldAttributes.get(key) ?? null))) { delete attributes[key] } else { attributes[key] = value @@ -785,7 +785,7 @@ export class YTextEvent extends YEvent { } } else if (this.deletes(item)) { oldAttributes.set(key, value) - const curVal = currentAttributes.get(key) || null + const curVal = currentAttributes.get(key) ?? null if (!equalAttrs(curVal, value)) { if (action === 'retain') { addOp() From 1e69d650b83fa73a964656bbf6ca3e2e9ba3dabb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 1 Mar 2024 11:31:21 +0100 Subject: [PATCH 129/362] 13.6.14 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f59a9202..3001c5226 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.13", + "version": "13.6.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.13", + "version": "13.6.14", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 43dee697e..d291b5258 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.13", + "version": "13.6.14", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 6beab79eb4fe852080122f6018c85a9cd8078c56 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 1 Mar 2024 11:39:31 +0100 Subject: [PATCH 130/362] add tests for falsy formatting attributes - #619 --- tests/y-text.tests.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 87ac21a4a..34ae7e9e7 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1746,6 +1746,27 @@ export const testBasicFormat = tc => { compare(users) } +/** + * @param {t.TestCase} tc + */ +export const testFalsyFormats = tc => { + const { users, text0 } = init(tc, { users: 2 }) + let delta + text0.observe(event => { + delta = event.delta + }) + text0.insert(0, 'abcde', { falsy: false }) + t.compare(text0.toDelta(), [{ insert: 'abcde', attributes: { falsy: false } }]) + t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }]) + text0.format(1, 3, { falsy: true }) + t.compare(text0.toDelta(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }]) + text0.format(2, 1, { falsy: false }) + t.compare(text0.toDelta(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }]) + compare(users) +} + /** * @param {t.TestCase} _tc */ From b235c57d76e9d1b353f1f46cb80c440de311f72f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 12 Mar 2024 16:22:12 +0100 Subject: [PATCH 131/362] add tinybase --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 31d131300..86ce843f7 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,11 @@ An ActionCable companion for Yjs clients. There is a fitting
Websocket backend, written in Python.
+
Tinybase
+
+The reactive data store for local-first apps. They support multiple CRDTs and + different network technologies. +
#### Persistence Providers From 2fba694cd4c605812914082a27b0bcfa1011dca0 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 14 Mar 2024 20:33:34 +0100 Subject: [PATCH 132/362] Add documentation & clarification to clone method #622 --- src/types/AbstractType.js | 4 ++++ src/types/YArray.js | 4 ++++ src/types/YMap.js | 4 ++++ src/types/YText.js | 4 ++++ src/types/YXmlElement.js | 4 ++++ src/types/YXmlFragment.js | 4 ++++ src/types/YXmlHook.js | 4 ++++ src/types/YXmlText.js | 4 ++++ 8 files changed, 32 insertions(+) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 4cc1bf8a1..8927548ef 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -316,6 +316,10 @@ export class AbstractType { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {AbstractType} */ clone () { diff --git a/src/types/YArray.js b/src/types/YArray.js index a895274e9..54a20c198 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -95,6 +95,10 @@ export class YArray extends AbstractType { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YArray} */ clone () { diff --git a/src/types/YMap.js b/src/types/YMap.js index 855ccb4f1..974e73164 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -88,6 +88,10 @@ export class YMap extends AbstractType { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YMap} */ clone () { diff --git a/src/types/YText.js b/src/types/YText.js index 8de9260e8..8919b009f 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -897,6 +897,10 @@ export class YText extends AbstractType { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YText} */ clone () { diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 1c1f3190e..48029f698 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -81,6 +81,10 @@ export class YXmlElement extends YXmlFragment { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YXmlElement} */ clone () { diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index b229a4acc..496c5ab67 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -163,6 +163,10 @@ export class YXmlFragment extends AbstractType { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YXmlFragment} */ clone () { diff --git a/src/types/YXmlHook.js b/src/types/YXmlHook.js index c5b5ed6dc..1bf248465 100644 --- a/src/types/YXmlHook.js +++ b/src/types/YXmlHook.js @@ -29,6 +29,10 @@ export class YXmlHook extends YMap { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YXmlHook} */ clone () { diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index 413b247cd..ab02dbf3e 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -30,6 +30,10 @@ export class YXmlText extends YText { } /** + * Makes a copy of this data type that can be included somewhere else. + * + * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. + * * @return {YXmlText} */ clone () { From 6e674ff5f71e8d84b07ef68f6837035f62bd1eaa Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 14 Mar 2024 21:09:34 +0100 Subject: [PATCH 133/362] add y-webxdc - related to yjs/docs#55 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 86ce843f7..e5a4c5d3e 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,10 @@ Websocket backend, written in Python. The reactive data store for local-first apps. They support multiple CRDTs and different network technologies. +
y-webxdc
+
+Provider for sharing data in webxdc chat apps. +
#### Persistence Providers From 2062f52a900547abbd5bd262bd7855dfb2cdd0fd Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 15 Mar 2024 01:42:07 +0100 Subject: [PATCH 134/362] add reference to y-redis --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e5a4c5d3e..627b84dfd 100644 --- a/README.md +++ b/README.md @@ -145,9 +145,9 @@ collaborative app.
y-websocket
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. y-sweet and ypy-websocket (see below) are -compatible to the y-wesocket protocol. +connects to that backend. y-redis, +y-sweet, and ypy-websocket (see below) are alternative +backends to y-websocket.
y-webrtc
From dc45a8d3cf66867cb2aa76d2a44119a976cce887 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 23 Mar 2024 12:43:54 +0100 Subject: [PATCH 135/362] [readme] Added AppMaster to "Who is Using" --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 627b84dfd..6cc6ebd85 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ 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. +* [AppMaster](https://appmaster.io) A No-Code platform for creating + production-ready applications with source code generation. ## Table of Contents From ca24f1ee765a68d1a50a2665a786cf486775da2c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 23 Mar 2024 14:29:23 +0100 Subject: [PATCH 136/362] added more sponsors --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cc6ebd85..957bf40ea 100644 --- a/README.md +++ b/README.md @@ -64,11 +64,15 @@ Showcase](https://yjs-diagram.synergy.codes/). Nimbus Web. :star: * [Pluxbox RadioManager](https://getradiomanager.com/) A web-based app to collaboratively organize radio broadcasts. :star: +* [modyfi](https://www.modyfi.com) - Modyfi is the design platform built for + multidisciplinary designers. Design, generate, animate, and more — without + switching between apps. :star: * [Sana](https://sanalabs.com/) A learning platform with collaborative text editing powered by Yjs. * [Serenity Notes](https://www.serenity.re/en/notes) End-to-end encrypted collaborative notes app. -* [PRSM](https://prsm.uk/) Collaborative mind-mapping and system visualisation. *[(source)](https://github.com/micrology/prsm)* +* [PRSM](https://prsm.uk/) Collaborative mind-mapping and system visualisation. + *[(source)](https://github.com/micrology/prsm)* * [Alldone](https://alldone.app/) A next-gen project management and collaboration platform. * [Living Spec](https://livingspec.com/) A modern way for product teams to collaborate. @@ -91,6 +95,12 @@ 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 +* [Multi.app](https://multi.app) - Multiplayer app sharing: Point, draw and edit + in shared apps as if they're on your computer. They are using Yrs. * [AppMaster](https://appmaster.io) A No-Code platform for creating production-ready applications with source code generation. From d730abe594ff1c149c75efb9c17549cf4b140eba Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 24 Mar 2024 21:00:23 +0100 Subject: [PATCH 137/362] add synthesia as a user --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 957bf40ea..1791686ef 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ Showcase](https://yjs-diagram.synergy.codes/). in shared apps as if they're on your computer. They are using Yrs. * [AppMaster](https://appmaster.io) A No-Code platform for creating production-ready applications with source code generation. +* [Synthesia](https://www.synthesia.io) - Collaborative Video Editor ## Table of Contents From d119459fad8404a27dfcef59cbee37818c0e2e0d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 3 Apr 2024 15:22:52 +0200 Subject: [PATCH 138/362] add huly as a user --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1791686ef..c6a9ab5c5 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,9 @@ Showcase](https://yjs-diagram.synergy.codes/). ## Who is using Yjs * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source - knowledge base. 🏅 + knowledge base. :start2: +* [Huly](https://huly.io/) - Open Source All-in-One Project Management Platform + :start2: * [Cargo](https://cargo.site/) Site builder for designers and artists :star2: * [Gitbook](https://gitbook.com) Knowledge management for technical teams :star2: * [Evernote](https://evernote.com) Note-taking app :star2: From 52b906898fee761a6223eeef6a33adc2a4041b80 Mon Sep 17 00:00:00 2001 From: Satyajeet Jadhav Date: Tue, 9 Apr 2024 16:39:18 +0530 Subject: [PATCH 139/362] Update Readme who-is-using (thinkdeli.com) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c6a9ab5c5..c81bc9231 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [AppMaster](https://appmaster.io) A No-Code platform for creating production-ready applications with source code generation. * [Synthesia](https://www.synthesia.io) - Collaborative Video Editor +* [thinkdeli](https://thinkdeli.com) - A fast and simple notes app powered by AI ## Table of Contents From 43e17802a607ca8f8cd2c4ea8a7b63ce28197a9d Mon Sep 17 00:00:00 2001 From: synix Date: Thu, 11 Apr 2024 11:21:14 +0800 Subject: [PATCH 140/362] fix: update search marker count in INTERNALS.md --- INTERNALS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INTERNALS.md b/INTERNALS.md index 4daf96594..3f4ae1db1 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -88,7 +88,7 @@ When a local insert happens, Yjs needs to map the insert position in the document (eg position 1000) to an ID. With just the linked list, this would require a slow O(n) linear scan of the list. But when editing a document, most inserts are either at the same position as the last insert, or nearby. To -improve performance, Yjs stores a cache of the 10 most recently looked up +improve performance, Yjs stores a cache of the 80 most recently looked up insert positions in the document. This is consulted and updated when a position is looked up to improve performance in the average case. The cache is updated using a heuristic that is still changing (currently, it is updated when a new From f5aa852054c39d441100efb290508ad329a29dbf Mon Sep 17 00:00:00 2001 From: synix Date: Sat, 13 Apr 2024 21:26:44 +0800 Subject: [PATCH 141/362] remove outdated Y instance in comments --- src/utils/Doc.js | 14 +++++++------- src/utils/Transaction.js | 5 +++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 8a8936b9d..62643617c 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -187,22 +187,22 @@ export class Doc extends ObservableV2 { /** * Define a shared data type. * - * Multiple calls of `y.get(name, TypeConstructor)` yield the same result + * Multiple calls of `ydoc.get(name, TypeConstructor)` yield the same result * and do not overwrite each other. I.e. - * `y.define(name, Y.Array) === y.define(name, Y.Array)` + * `ydoc.get(name, Y.Array) === ydoc.get(name, Y.Array)` * - * After this method is called, the type is also available on `y.share.get(name)`. + * After this method is called, the type is also available on `ydoc.share.get(name)`. * * *Best Practices:* - * Define all types right after the Yjs instance is created and store them in a separate object. + * Define all types right after the Y.Doc instance is created and store them in a separate object. * Also use the typed methods `getText(name)`, `getArray(name)`, .. * * @template {typeof AbstractType} Type * @example - * const y = new Y(..) + * const ydoc = new Y.Doc(..) * const appState = { - * document: y.getText('document') - * comments: y.getArray('comments') + * document: ydoc.getText('document') + * comments: ydoc.getArray('comments') * } * * @param {string} name diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index c5931ab44..829589ece 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -28,7 +28,8 @@ import { callAll } from 'lib0/function' * possible. Here is an example to illustrate the advantages of bundling: * * @example - * const map = y.define('map', YMap) + * const ydoc = new Y.Doc() + * const map = ydoc.getMap('map') * // Log content when change is triggered * map.observe(() => { * console.log('change triggered') @@ -37,7 +38,7 @@ import { callAll } from 'lib0/function' * map.set('a', 0) // => "change triggered" * map.set('b', 0) // => "change triggered" * // When put in a transaction, it will trigger the log after the transaction: - * y.transact(() => { + * ydoc.transact(() => { * map.set('a', 1) * map.set('b', 1) * }) // => "change triggered" From f1532771b7534273d99ef89df598176f170799e8 Mon Sep 17 00:00:00 2001 From: saki Date: Tue, 16 Apr 2024 01:15:02 +0900 Subject: [PATCH 142/362] fix typo --- src/types/AbstractType.js | 4 ++-- src/types/YArray.js | 2 +- src/types/YXmlFragment.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 8927548ef..3dff240c2 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -481,7 +481,7 @@ export const typeListToArraySnapshot = (type, snapshot) => { } /** - * Executes a provided function on once on overy element of this YArray. + * Executes a provided function on once on every element of this YArray. * * @param {AbstractType} type * @param {function(any,number,any):void} f A function to execute on every element of this YArray. @@ -573,7 +573,7 @@ export const typeListCreateIterator = type => { } /** - * Executes a provided function on once on overy element of this YArray. + * Executes a provided function on once on every element of this YArray. * Operates on a snapshotted state of the document. * * @param {AbstractType} type diff --git a/src/types/YArray.js b/src/types/YArray.js index 54a20c198..ce60de78d 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -248,7 +248,7 @@ export class YArray extends AbstractType { } /** - * Executes a provided function once on overy element of this YArray. + * Executes a provided function once on every element of this YArray. * * @param {function(T,number,YArray):void} f A function to execute on every element of this YArray. */ diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 496c5ab67..2f37684a8 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -410,7 +410,7 @@ export class YXmlFragment extends AbstractType { } /** - * Executes a provided function on once on overy child element. + * Executes a provided function on once on every child element. * * @param {function(YXmlElement|YXmlText,number, typeof self):void} f A function to execute on every element of this YArray. */ From 4ffd23fd0be7401053673882096c096438f7fd01 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 17 Apr 2024 20:41:42 +0200 Subject: [PATCH 143/362] typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c81bc9231..97070b703 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,9 @@ Showcase](https://yjs-diagram.synergy.codes/). ## Who is using Yjs * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source - knowledge base. :start2: + knowledge base. :star2: * [Huly](https://huly.io/) - Open Source All-in-One Project Management Platform - :start2: + :star2: * [Cargo](https://cargo.site/) Site builder for designers and artists :star2: * [Gitbook](https://gitbook.com) Knowledge management for technical teams :star2: * [Evernote](https://evernote.com) Note-taking app :star2: From 5e712e39b1f98002a3517860c1a24322159b54db Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 24 Apr 2024 16:03:21 +0200 Subject: [PATCH 144/362] add ourboard as user --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 97070b703..0c7c773f4 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ Showcase](https://yjs-diagram.synergy.codes/). production-ready applications with source code generation. * [Synthesia](https://www.synthesia.io) - Collaborative Video Editor * [thinkdeli](https://thinkdeli.com) - A fast and simple notes app powered by AI +* [ourboard](https://github.com/raimohanska/ourboard) - A collaborative whiteboard + applicaiton ## Table of Contents From 25ae9f3236ec3b7acb27a7f81b897b8bf647c950 Mon Sep 17 00:00:00 2001 From: synix Date: Thu, 25 Apr 2024 11:03:17 +0800 Subject: [PATCH 145/362] remove unused _transaction in YArray --- src/types/YArray.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/types/YArray.js b/src/types/YArray.js index ce60de78d..217c154ef 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -32,7 +32,6 @@ export class YArrayEvent extends YEvent { */ constructor (yarray, transaction) { super(yarray, transaction) - this._transaction = transaction } } From f0dc53f53f4c2fdf90a2701df73f73b6c2f06395 Mon Sep 17 00:00:00 2001 From: synix Date: Thu, 25 Apr 2024 11:17:49 +0800 Subject: [PATCH 146/362] fix minor typos --- src/utils/Transaction.js | 2 +- src/utils/encoding.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 829589ece..9792d32ed 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -225,7 +225,7 @@ const tryGcDeleteSet = (ds, store, gcFilter) => { */ 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 + // merge from right to left for better efficiency and so we don't miss any merge targets ds.clients.forEach((deleteItems, client) => { const structs = /** @type {Array} */ (store.clients.get(client)) for (let di = deleteItems.length - 1; di >= 0; di--) { diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 2277a1407..08a9602db 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -154,7 +154,7 @@ export const readClientsStructRefs = (decoder, doc) => { // @type {string|null} const struct = new Item( createID(client, clock), - null, // leftd + null, // left (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null, // origin null, // right (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin @@ -178,7 +178,7 @@ export const readClientsStructRefs = (decoder, doc) => { const struct = new Item( createID(client, clock), - null, // leftd + null, // left origin, // origin null, // right rightOrigin, // right origin @@ -370,7 +370,7 @@ export const writeStructsFromTransaction = (encoder, transaction) => writeClient /** * Read and apply a document update. * - * This function has the same effect as `applyUpdate` but accepts an decoder. + * This function has the same effect as `applyUpdate` but accepts a decoder. * * @param {decoding.Decoder} decoder * @param {Doc} ydoc @@ -451,7 +451,7 @@ export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = n /** * Read and apply a document update. * - * This function has the same effect as `applyUpdate` but accepts an decoder. + * This function has the same effect as `applyUpdate` but accepts a decoder. * * @param {decoding.Decoder} decoder * @param {Doc} ydoc From 43815d8292d19be4ec2d3cfed4574bef3521ac9a Mon Sep 17 00:00:00 2001 From: synix Date: Thu, 25 Apr 2024 11:33:36 +0800 Subject: [PATCH 147/362] fix lint error --- src/types/YArray.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/types/YArray.js b/src/types/YArray.js index 217c154ef..2afdb7e6c 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -25,15 +25,7 @@ import { typeListSlice } from './AbstractType.js' * @template T * @extends YEvent> */ -export class YArrayEvent extends YEvent { - /** - * @param {YArray} yarray The changed type - * @param {Transaction} transaction The transaction object - */ - constructor (yarray, transaction) { - super(yarray, transaction) - } -} +export class YArrayEvent extends YEvent {} /** * A shared Array implementation. From 8270373c9fe42dc1ffee106b3e7afd41fc863063 Mon Sep 17 00:00:00 2001 From: Felix Salazar Date: Thu, 25 Apr 2024 19:34:05 +0200 Subject: [PATCH 148/362] Add Hocuspocus as a backend provider --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c7c773f4..d3a1d6609 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ collaborative app.
A module that contains a simple websocket backend and a websocket client that connects to that backend. y-redis, -y-sweet, and ypy-websocket (see below) are alternative +y-sweet, ypy-websocket and Hocuspocus (see below) are alternative backends to y-websocket.
y-webrtc
@@ -187,6 +187,10 @@ browser DevTools extension.
A standalone yjs server with persistence to S3 or filesystem. They offer a cloud service as well. +
+
Hocuspocus
+
+A standalone extensible yjs server with sqlite persistence, webhooks, auth and more.
PartyKit
From 387be70ae96e884dab468f73aaf776616b062d0b Mon Sep 17 00:00:00 2001 From: synix Date: Fri, 26 Apr 2024 11:49:52 +0800 Subject: [PATCH 149/362] make slice() function's doc more accurate --- src/types/YArray.js | 6 +++--- src/types/YXmlFragment.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/types/YArray.js b/src/types/YArray.js index 2afdb7e6c..609247bd6 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -162,9 +162,9 @@ export class YArray extends AbstractType { } /** - * Preppends content to this YArray. + * Prepends content to this YArray. * - * @param {Array} content Array of content to preppend. + * @param {Array} content Array of content to prepend. */ unshift (content) { this.insert(0, content) @@ -206,7 +206,7 @@ export class YArray extends AbstractType { } /** - * Transforms this YArray to a JavaScript Array. + * Returns a portion of this YArray into a JavaScript Array selected from start to end (end not included). * * @param {number} [start] * @param {number} [end] diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 2f37684a8..16b549850 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -380,9 +380,9 @@ export class YXmlFragment extends AbstractType { } /** - * Preppends content to this YArray. + * Prepends content to this YArray. * - * @param {Array} content Array of content to preppend. + * @param {Array} content Array of content to prepend. */ unshift (content) { this.insert(0, content) @@ -399,7 +399,7 @@ export class YXmlFragment extends AbstractType { } /** - * Transforms this YArray to a JavaScript Array. + * Returns a portion of this YXmlFragment into a JavaScript Array selected from start to end (end not included). * * @param {number} [start] * @param {number} [end] From 3df335cb4cc1db585c2eb7af8942e99e9256ece2 Mon Sep 17 00:00:00 2001 From: synix Date: Fri, 26 Apr 2024 12:03:28 +0800 Subject: [PATCH 150/362] update slice() function's doc --- src/types/YArray.js | 3 ++- src/types/YXmlFragment.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/types/YArray.js b/src/types/YArray.js index 609247bd6..38b4e11f5 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -206,7 +206,8 @@ export class YArray extends AbstractType { } /** - * Returns a portion of this YArray into a JavaScript Array selected from start to end (end not included). + * Returns a portion of this YArray into a JavaScript Array selected + * from start to end (end not included). * * @param {number} [start] * @param {number} [end] diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 16b549850..1445139cd 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -399,7 +399,8 @@ export class YXmlFragment extends AbstractType { } /** - * Returns a portion of this YXmlFragment into a JavaScript Array selected from start to end (end not included). + * Returns a portion of this YXmlFragment into a JavaScript Array selected + * from start to end (end not included). * * @param {number} [start] * @param {number} [end] From 0af69cf6d6ac6be029f749ce507783299bdcccae Mon Sep 17 00:00:00 2001 From: synix Date: Fri, 26 Apr 2024 13:15:06 +0800 Subject: [PATCH 151/362] fix: markdownlint readme error --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d3a1d6609..80acf0256 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,8 @@ collaborative app.
A module that contains a simple websocket backend and a websocket client that connects to that backend. y-redis, -y-sweet, ypy-websocket and Hocuspocus (see below) are alternative +y-sweet, ypy-websocket and +Hocuspocus (see below) are alternative backends to y-websocket.
y-webrtc
From ce43124ad0516e95a3a1075148264758762574de Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 27 Apr 2024 00:24:49 +0200 Subject: [PATCH 152/362] [relative-positions] add option to configure whether to follow redon insertions - #638 --- src/utils/RelativePosition.js | 18 +++++++++++++++--- tests/relativePositions.tests.js | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index a1b4356b3..744dff370 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -8,6 +8,7 @@ import { createID, ContentType, followRedone, + getItem, ID, Doc, AbstractType // eslint-disable-line } from '../internals.js' @@ -256,13 +257,24 @@ export const readRelativePosition = decoder => { export const decodeRelativePosition = uint8Array => readRelativePosition(decoding.createDecoder(uint8Array)) /** + * Transform a relative position to an absolute position. + * + * If you want to share the relative position with other users, you should set + * `followUndoneDeletions` to false to get consistent results across all clients. + * + * When calculating the absolute position, we try to follow the "undone deletions". This yields + * better results for the user who performed undo. However, only the user who performed the undo + * will get the better results, the other users don't know which operations recreated a deleted + * range of content. There is more information in this ticket: https://github.com/yjs/yjs/issues/638 + * * @param {RelativePosition} rpos * @param {Doc} doc + * @param {boolean} followUndoneDeletions - whether to follow undone deletions - see https://github.com/yjs/yjs/issues/638 * @return {AbsolutePosition|null} * * @function */ -export const createAbsolutePositionFromRelativePosition = (rpos, doc) => { +export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndoneDeletions = true) => { const store = doc.store const rightID = rpos.item const typeID = rpos.type @@ -274,7 +286,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => { if (getState(store, rightID.client) <= rightID.clock) { return null } - const res = followRedone(store, rightID) + const res = followUndoneDeletions ? followRedone(store, rightID) : { item: getItem(store, rightID), diff: 0 } const right = res.item if (!(right instanceof Item)) { return null @@ -298,7 +310,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => { // type does not exist yet return null } - const { item } = followRedone(store, typeID) + const { item } = followUndoneDeletions ? followRedone(store, typeID) : { item: getItem(store, typeID) } if (item instanceof Item && item.content instanceof ContentType) { type = item.content.type } else { diff --git a/tests/relativePositions.tests.js b/tests/relativePositions.tests.js index 93fec2308..ab86168b5 100644 --- a/tests/relativePositions.tests.js +++ b/tests/relativePositions.tests.js @@ -101,3 +101,25 @@ export const testRelativePositionAssociationDifference = tc => { t.assert(posRight != null && posRight.index === 2) t.assert(posLeft != null && posLeft.index === 1) } + +/** + * @param {t.TestCase} tc + */ +export const testRelativePositionWithUndo = tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + ytext.insert(0, 'hello world') + const rpos = Y.createRelativePositionFromTypeIndex(ytext, 1) + const um = new Y.UndoManager(ytext) + ytext.delete(0, 6) + t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydoc)?.index === 0) + um.undo() + t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydoc)?.index === 1) + const posWithoutFollow = Y.createAbsolutePositionFromRelativePosition(rpos, ydoc, false) + console.log({ posWithoutFollow }) + t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydoc, false)?.index === 6) + const ydocClone = new Y.Doc() + Y.applyUpdate(ydocClone, Y.encodeStateAsUpdate(ydoc)) + t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydocClone)?.index === 6) + t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydocClone, false)?.index === 6) +} From 91b718cde076a76e0f028b4f21a143bd3064297a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 27 Apr 2024 00:50:32 +0200 Subject: [PATCH 153/362] 13.6.15 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3001c5226..94573a13d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.14", + "version": "13.6.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.14", + "version": "13.6.15", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index d291b5258..6d35562f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.14", + "version": "13.6.15", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 656b7e7f6a33dfef9f5ff844aa6572d02e6430c6 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 28 Apr 2024 21:16:58 +0200 Subject: [PATCH 154/362] add more users --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 80acf0256..32e35dcd8 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,9 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Evernote](https://evernote.com) Note-taking app :star2: * [Lessonspace](https://thelessonspace.com) Enterprise platform for virtual classrooms and online training :star2: +* [Ellipsus]{ellipsus.com} - Collaborative writing app for storytelling etc. + Supports versioning, change attribution, and "blame". A solution for the whole + publishing process (also selling) :star: * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star: * [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and community. :star: @@ -109,6 +112,8 @@ Showcase](https://yjs-diagram.synergy.codes/). * [thinkdeli](https://thinkdeli.com) - A fast and simple notes app powered by AI * [ourboard](https://github.com/raimohanska/ourboard) - A collaborative whiteboard applicaiton +* [Ellie.ai](https://ellie.ai) - Data Product Design and Collaboration +* [GoPeer](https://gopeer.org/) - Collaborative tutoring ## Table of Contents From 54594a2d7546aef289542a7189edfa5bb0cade2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20Jan=C3=9Fen?= Date: Tue, 30 Apr 2024 16:29:06 +0200 Subject: [PATCH 155/362] Add y-op-sqlite to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5c20b5082..ebec926f5 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,10 @@ Like y-indexeddb, but with sub-documents support and fully TypeScript.
A database and connection provider for Yjs based on Firestore.
+
y-op-sqlite
+
+Persist YJS updates in your React Native app using op-sqlite, the fastest SQLite library for React Native. +
# Ports From 06e71f651d7364630bcc93610e497ea1e3429ae8 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 9 May 2024 05:55:26 -0500 Subject: [PATCH 156/362] Fix y-websocket server path The commmit https://github.com/yjs/y-websocket/commit/c3d14cf07d8628431a7480213131ce58cad9adc9 renamed `server.js` to `server.cjs`; mirror that change here. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c20b5082..a9d109a73 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,7 @@ npm i yjs y-websocket Start the y-websocket server: ```sh -PORT=1234 node ./node_modules/y-websocket/bin/server.js +PORT=1234 node ./node_modules/y-websocket/bin/server.cjs ``` ### Example: Observe types From edad668dbdf64b590777b6086c00c27be0d27c09 Mon Sep 17 00:00:00 2001 From: i12345 Date: Wed, 15 May 2024 13:43:59 -0500 Subject: [PATCH 157/362] Update INTERNALS.md explained 53 bit JS numbers --- INTERNALS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INTERNALS.md b/INTERNALS.md index 3f4ae1db1..8ec81c077 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -26,7 +26,7 @@ article](https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing/). Each client is assigned a unique *clientID* property on first insert. This is a random 53-bit integer (53 bits because that fits in the javascript safe integer -range). +range \[JavaScript uses IEEE 754 floats\]). ## List items From d67a951104d09685f86bf6870e07c93b9d5974d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20N=C3=B6tzold?= <69591795+MaxNoetzold@users.noreply.github.com> Date: Tue, 21 May 2024 15:30:24 +0200 Subject: [PATCH 158/362] add y-postgresql info to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 6e0a6377e..e87792fb3 100644 --- a/README.md +++ b/README.md @@ -272,6 +272,10 @@ A database and connection provider for Yjs based on Firestore.
Persist YJS updates in your React Native app using op-sqlite, the fastest SQLite library for React Native.
+
y-postgresql
+
+Provides persistent storage for a web server using PostgreSQL and is easily compatible with y-websocket. +
# Ports From 03593aeeb15dcee35a8108d82f8fd912f27f15ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20N=C3=B6tzold?= <69591795+MaxNoetzold@users.noreply.github.com> Date: Tue, 21 May 2024 16:03:31 +0200 Subject: [PATCH 159/362] fix linting errors --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e87792fb3..85748eee2 100644 --- a/README.md +++ b/README.md @@ -270,11 +270,14 @@ A database and connection provider for Yjs based on Firestore.
y-op-sqlite
-Persist YJS updates in your React Native app using op-sqlite, the fastest SQLite library for React Native. + Persist YJS updates in your React Native app using + op-sqlite + , the fastest SQLite library for React Native.
y-postgresql
-Provides persistent storage for a web server using PostgreSQL and is easily compatible with y-websocket. + Provides persistent storage for a web server using PostgreSQL and + is easily compatible with y-websocket.
From 6932696795fff7f0c6b3c23a24b972a10a85320d Mon Sep 17 00:00:00 2001 From: Fuad Saud Date: Thu, 6 Jun 2024 13:38:08 +0200 Subject: [PATCH 160/362] Export mergeDeleteSets Useful for comparing snapshots. --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index ae96b747c..b13b2077b 100644 --- a/src/index.js +++ b/src/index.js @@ -100,6 +100,7 @@ export { UpdateDecoderV1, UpdateDecoderV2, equalDeleteSets, + mergeDeleteSets, snapshotContainsUpdate } from './internals.js' From 0678ed1eb520f471f46c2f8a4f929565c2d622f9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 10 Jun 2024 12:18:16 +0200 Subject: [PATCH 161/362] fix event.path in observeDeep - closes #457 --- src/utils/YEvent.js | 4 ++-- tests/y-array.tests.js | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 131741488..d6a602429 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -264,8 +264,8 @@ const getPathTo = (parent, child) => { let i = 0 let c = /** @type {AbstractType} */ (child._item.parent)._start while (c !== child._item && c !== null) { - if (!c.deleted) { - i++ + if (!c.deleted && c.countable) { + i += c.length } c = c.right } diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 941598ac9..9875e85e2 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -330,6 +330,29 @@ export const testObserveDeepEventOrder = tc => { } } +/** + * Correct index when computing event.path in observeDeep - https://github.com/yjs/yjs/issues/457 + * + * @param {t.TestCase} _tc + */ +export const testObservedeepIndexes = _tc => { + const doc = new Y.Doc() + const map = doc.getMap() + // Create a field with the array as value + map.set('my-array', new Y.Array()) + // Fill the array with some strings and our Map + map.get('my-array').push(['a', 'b', 'c', new Y.Map()]) + /** + * @type {Array} + */ + let eventPath = [] + map.observeDeep((events) => { eventPath = events[0].path }) + // set a value on the map inside of our array + map.get('my-array').get(3).set('hello', 'world') + console.log(eventPath) + t.compare(eventPath, ['my-array', 3]) +} + /** * @param {t.TestCase} tc */ From fbd088ee785cf6e22c5d009b436473f14210dfeb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 10 Jun 2024 12:21:06 +0200 Subject: [PATCH 162/362] 13.6.16 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 94573a13d..6c3fe7d77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.15", + "version": "13.6.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.15", + "version": "13.6.16", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 6d35562f6..795aab1ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.15", + "version": "13.6.16", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 88506f6d789def87043a3dcabcd20fa7872eb14f Mon Sep 17 00:00:00 2001 From: Sebastian Szvetecz Date: Wed, 12 Jun 2024 01:12:13 +0200 Subject: [PATCH 163/362] Fixed star icon in README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 85748eee2..35e5ca808 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source knowledge base. :star2: -* [Huly](https://huly.io/) - Open Source All-in-One Project Management Platform - :star2: +* [Huly](https://huly.io/) - Open Source All-in-One Project Management Platform :star2: * [Cargo](https://cargo.site/) Site builder for designers and artists :star2: * [Gitbook](https://gitbook.com) Knowledge management for technical teams :star2: * [Evernote](https://evernote.com) Note-taking app :star2: From 2e79d0369ecc73305c361d9baad4b8aaf7d0027b Mon Sep 17 00:00:00 2001 From: Sebastian Szvetecz Date: Wed, 12 Jun 2024 01:12:48 +0200 Subject: [PATCH 164/362] Fixed markdown link for ellipsus.org in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35e5ca808..4626fd154 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Evernote](https://evernote.com) Note-taking app :star2: * [Lessonspace](https://thelessonspace.com) Enterprise platform for virtual classrooms and online training :star2: -* [Ellipsus]{ellipsus.com} - Collaborative writing app for storytelling etc. +* [Ellipsus](ellipsus.com) - Collaborative writing app for storytelling etc. Supports versioning, change attribution, and "blame". A solution for the whole publishing process (also selling) :star: * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star: From d4dac558c036f34000fd565b7961ed27e6f8f352 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 17 Jun 2024 15:12:31 +0200 Subject: [PATCH 165/362] fix creating relative position from json when type name is the empty string --- src/utils/RelativePosition.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index 744dff370..214fc6f0e 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -102,7 +102,7 @@ export const relativePositionToJSON = rpos => { * * @function */ -export const createRelativePositionFromJSON = json => new RelativePosition(json.type == null ? null : createID(json.type.client, json.type.clock), json.tname || null, json.item == null ? null : createID(json.item.client, json.item.clock), json.assoc == null ? 0 : json.assoc) +export const createRelativePositionFromJSON = json => new RelativePosition(json.type == null ? null : createID(json.type.client, json.type.clock), json.tname ?? null, json.item == null ? null : createID(json.item.client, json.item.clock), json.assoc == null ? 0 : json.assoc) export class AbsolutePosition { /** From 34b06b6cf918266bbd4f646f193b9fa3d408089b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 17 Jun 2024 15:15:04 +0200 Subject: [PATCH 166/362] 13.6.17 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c3fe7d77..cb163c673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.16", + "version": "13.6.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.16", + "version": "13.6.17", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 795aab1ed..7ad73ac8d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.16", + "version": "13.6.17", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 3f1746f3a9323c951794ab1142de8a7a56ed8077 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 17 Jun 2024 20:29:14 +0200 Subject: [PATCH 167/362] add lexical editor --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4626fd154..da7d827ee 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,8 @@ are implemented in separate modules. | [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](https://github.com/yjs/y-monaco) | [demo](https://demos.yjs.dev/monaco/monaco.html) | | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | +| [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | + | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | | React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) | From 2e5abad7739288c6c3ccd5488dd28941da9982d2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Jun 2024 16:51:57 +0200 Subject: [PATCH 168/362] fix #645 yjs/y-utility#8 --- README.md | 1 - src/utils/UndoManager.js | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index da7d827ee..e19b9dc81 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,6 @@ are implemented in separate modules. | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | - | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | | React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) | diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 27023ecfa..a3645cd87 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -118,12 +118,13 @@ const popStackItem = (undoManager, stack, eventType) => { }) _tr = transaction }, undoManager) - if (undoManager.currStackItem != null) { + const res = undoManager.currStackItem + if (res != null) { const changedParentTypes = _tr.changedParentTypes - undoManager.emit('stack-item-popped', [{ stackItem: undoManager.currStackItem, type: eventType, changedParentTypes, origin: undoManager }, undoManager]) + undoManager.emit('stack-item-popped', [{ stackItem: res, type: eventType, changedParentTypes, origin: undoManager }, undoManager]) undoManager.currStackItem = null } - return undoManager.currStackItem + return res } /** From 1bfa6dfb74704d527f74f7f06f4f92a9eb66be2c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 18 Jun 2024 16:59:36 +0200 Subject: [PATCH 169/362] 13.6.18 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb163c673..7d95ce388 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.17", + "version": "13.6.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.17", + "version": "13.6.18", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 7ad73ac8d..16c95a9ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.17", + "version": "13.6.18", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 5e19c35405ab20bf63fe75abe93fe404b5128819 Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Mon, 24 Jun 2024 13:35:55 +0200 Subject: [PATCH 170/362] add react-yjs to bindings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e19b9dc81..b7265b428 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ are implemented in separate modules. | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | +| React | | [react-yjs](https://github.com/nikgraf/react-yjs) | [demo](https://react-yjs-example.vercel.app/) | | 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) | From f29cd2baf44a45f3664b1be1bf0b9bc9c9cb9915 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 10 Jul 2024 17:52:23 +0200 Subject: [PATCH 171/362] update users --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b7265b428..4378f5846 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,9 @@ Showcase](https://yjs-diagram.synergy.codes/). applicaiton * [Ellie.ai](https://ellie.ai) - Data Product Design and Collaboration * [GoPeer](https://gopeer.org/) - Collaborative tutoring -* [screen.garden](https://screen.garden) Collaborative backend for PKM apps. +* [screen.garden](https://screen.garden) - Collaborative backend for PKM apps. +* [NextCloud](https://nextcloud.com/) - Content Collaboration Platform +* [keystatic](https://github.com/Thinkmill/keystatic) - git-based CMS ## Table of Contents From c944a4553c94be1a4b87fd35e950938f0f2900c8 Mon Sep 17 00:00:00 2001 From: Mikko Reinikainen Date: Tue, 30 Jul 2024 12:55:11 +0300 Subject: [PATCH 172/362] Add Y.Array.from() and yarray.clone() to API docs --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4378f5846..311427802 100644 --- a/README.md +++ b/README.md @@ -412,6 +412,8 @@ necessary.

const yarray = new Y.Array()
+ Y.Array.from(Array<object|boolean|Array|string|number|null|Uint8Array|Y.Type>): Y.Array +
An alternative factory function to create a Y.Array based on existing content.
parent:Y.AbstractType|null
insert(index:number, content:Array<object|boolean|Array|string|number|null|Uint8Array|Y.Type>) @@ -441,6 +443,8 @@ forEach(function(value:object|boolean|Array|string|number|null|Uint8Array|Y.Type
map(function(T, number, YArray):M):Array<M>
+ clone(): Y.Array +
Clone all values into a fresh Y.Array instance. The returned type can be included into the Yjs document.
toArray():Array<object|boolean|Array|string|number|null|Uint8Array|Y.Type>
Copies the content of this YArray to a new Array.
toJSON():Array<Object|boolean|Array|string|number|null> From 294c6a15c5ee9de478fbbe45f9bdcbed03842dbd Mon Sep 17 00:00:00 2001 From: Mikko Reinikainen Date: Tue, 30 Jul 2024 12:55:45 +0300 Subject: [PATCH 173/362] Remove erroneous ymap.get(index:number) from API docs --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 311427802..5f6cfb0aa 100644 --- a/README.md +++ b/README.md @@ -501,8 +501,6 @@ or any of its children.
has(key:string):boolean
- get(index:number) -
clear()
Removes all elements from this YMap.
clone():Y.Map From eeb4c9969d68baa5b42c1b52cb15372bad7785c5 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 5 Aug 2024 16:14:47 +0200 Subject: [PATCH 174/362] lint readme --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f6cfb0aa..502d28693 100644 --- a/README.md +++ b/README.md @@ -412,7 +412,10 @@ necessary.

const yarray = new Y.Array()
- Y.Array.from(Array<object|boolean|Array|string|number|null|Uint8Array|Y.Type>): Y.Array + +Y.Array.from(Array<object|boolean|Array|string|number|null|Uint8Array|Y.Type>): +Y.Array +
An alternative factory function to create a Y.Array based on existing content.
parent:Y.AbstractType|null
@@ -444,7 +447,10 @@ forEach(function(value:object|boolean|Array|string|number|null|Uint8Array|Y.Type map(function(T, number, YArray):M):Array<M>
clone(): Y.Array -
Clone all values into a fresh Y.Array instance. The returned type can be included into the Yjs document.
+
+Clone all values into a fresh Y.Array instance. The returned type can be +included into the Yjs document. +
toArray():Array<object|boolean|Array|string|number|null|Uint8Array|Y.Type>
Copies the content of this YArray to a new Array.
toJSON():Array<Object|boolean|Array|string|number|null> From dd17228a8f410bfe5d3ef2e853973a1e678a3bd8 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 6 Aug 2024 16:37:52 +0200 Subject: [PATCH 175/362] update markdownlint --- package-lock.json | 646 +++++++++++++++++++++++++++++++++++----------- package.json | 2 +- 2 files changed, 495 insertions(+), 153 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7d95ce388..e01df3321 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "concurrently": "^3.6.1", "http-server": "^0.12.3", "jsdoc": "^3.6.7", - "markdownlint-cli": "^0.23.2", + "markdownlint-cli": "^0.41.0", "rollup": "^3.20.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", @@ -166,12 +166,89 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "24.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", @@ -773,13 +850,12 @@ } }, "node_modules/deep-extend": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", - "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=4.0.0" } }, "node_modules/deep-is": { @@ -882,6 +958,12 @@ "domelementtype": "1" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ecstatic": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", @@ -1791,6 +1873,22 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1873,12 +1971,15 @@ } }, "node_modules/get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", "dev": true, "engines": { - "node": ">=0.12.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-symbol-description": { @@ -1977,12 +2078,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, "node_modules/has": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", @@ -2148,9 +2243,9 @@ } }, "node_modules/ignore": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.9.tgz", - "integrity": "sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -2198,10 +2293,13 @@ "dev": true }, "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/internal-slot": { "version": "1.0.7", @@ -2499,6 +2597,21 @@ "url": "https://github.com/sponsors/dmonad" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2611,11 +2724,20 @@ } }, "node_modules/jsonc-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", - "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", "dev": true }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -2742,12 +2864,6 @@ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true }, - "node_modules/lodash.differencewith": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.differencewith/-/lodash.differencewith-4.5.0.tgz", - "integrity": "sha512-/8JFjydAS+4bQuo3CpLMBv7WxGFyk7/etOAsrQUCu0a9QVDemxv0YQ0rFyeZvqlUD314SERfNlgnlqqHmaQ0Cg==", - "dev": true - }, "node_modules/lodash.filter": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", @@ -2871,145 +2987,164 @@ } }, "node_modules/markdownlint": { - "version": "0.20.4", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.20.4.tgz", - "integrity": "sha512-jpfaPgjT0OpeBbemjYNZbzGG3hCLcAIvrm/pEY3+q/szDScG6ZonDacqySVRJAv9glbo8y4wBPJ0wgW17+9GGA==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz", + "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==", "dev": true, "dependencies": { - "markdown-it": "10.0.0" + "markdown-it": "14.1.0", + "markdownlint-micromark": "0.1.9" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, "node_modules/markdownlint-cli": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.23.2.tgz", - "integrity": "sha512-OSl5OZ8xzGN6z355cqRkiq67zPi3reJimklaF72p0554q85Dng5ToOjjSB9tDKZebSt85jX8cp+ruoQlPqOsPA==", - "dev": true, - "dependencies": { - "commander": "~2.9.0", - "deep-extend": "~0.5.1", - "get-stdin": "~5.0.1", - "glob": "~7.1.2", - "ignore": "~5.1.4", - "js-yaml": "~3.13.1", - "jsonc-parser": "~2.2.0", - "lodash.differencewith": "~4.5.0", - "lodash.flatten": "~4.4.0", - "markdownlint": "~0.20.4", - "markdownlint-rule-helpers": "~0.11.0", - "minimatch": "~3.0.4", - "minimist": "~1.2.5", - "rc": "~1.2.7" + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz", + "integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==", + "dev": true, + "dependencies": { + "commander": "~12.1.0", + "get-stdin": "~9.0.0", + "glob": "~10.4.1", + "ignore": "~5.3.1", + "js-yaml": "^4.1.0", + "jsonc-parser": "~3.2.1", + "jsonpointer": "5.0.1", + "markdownlint": "~0.34.0", + "minimatch": "~9.0.4", + "run-con": "~1.3.2", + "smol-toml": "~1.2.0" }, "bin": { "markdownlint": "markdownlint.js" }, "engines": { - "node": ">=10" - } - }, - "node_modules/markdownlint-cli/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": ">=18" } }, "node_modules/markdownlint-cli/node_modules/commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, - "dependencies": { - "graceful-readlink": ">= 1.0.0" - }, "engines": { - "node": ">= 0.6.x" + "node": ">=18" } }, "node_modules/markdownlint-cli/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/markdownlint-cli/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/markdownlint-rule-helpers": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.11.0.tgz", - "integrity": "sha512-PhGii9dOiDJDXxiRMpK8N0FM9powprvRPsXALgkjlSPTwLh6ymH+iF3iUe3nq8KGu26tclFBlLL5xAGy/zb7FA==", - "dev": true - }, - "node_modules/markdownlint/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/markdownlint-micromark": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz", + "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==", "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, "node_modules/markdownlint/node_modules/entities": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", - "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", - "dev": true + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, "node_modules/markdownlint/node_modules/linkify-it": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", - "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/markdownlint/node_modules/markdown-it": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", - "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "entities": "~2.0.0", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/markdownlint/node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true + }, + "node_modules/markdownlint/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, "node_modules/marked": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", @@ -3061,6 +3196,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -3280,6 +3424,12 @@ "node": ">=4" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3338,6 +3488,28 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -3555,6 +3727,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", @@ -3570,39 +3751,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3802,6 +3950,21 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, "node_modules/rx": { "version": "2.3.24", "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", @@ -3948,6 +4111,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -3998,6 +4173,15 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/smol-toml": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.2.tgz", + "integrity": "sha512-fVEjX2ybKdJKzFL46VshQbj9PuA4IUKivalgp48/3zwS9vXzyykzQ6AX92UxHSvWJagziMRLeHMgEzoGO7A8hQ==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, "node_modules/spawn-command": { "version": "0.0.2-1", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", @@ -4142,6 +4326,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -4219,6 +4418,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -4575,6 +4787,136 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 16c95a9ad..68bb42b48 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "concurrently": "^3.6.1", "http-server": "^0.12.3", "jsdoc": "^3.6.7", - "markdownlint-cli": "^0.23.2", + "markdownlint-cli": "^0.41.0", "rollup": "^3.20.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", From 44e51080afab40c8ee1441766e31de0a2b9b72ce Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 6 Aug 2024 16:48:14 +0200 Subject: [PATCH 176/362] fix new lint issues --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 502d28693..8c21c464a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > A CRDT framework with a powerful abstraction of shared data -Yjs is a [CRDT implementation](#Yjs-CRDT-Algorithm) that exposes its internal +Yjs is a [CRDT implementation](#yjs-crdt-algorithm) that exposes its internal data structure as *shared types*. Shared types are common data types like `Map` or `Array` with superpowers: changes are automatically distributed to other peers and merged without merge conflicts. @@ -119,19 +119,19 @@ Showcase](https://yjs-diagram.synergy.codes/). ## Table of Contents -* [Overview](#Overview) - * [Bindings](#Bindings) - * [Providers](#Providers) - * [Ports](#Ports) -* [Getting Started](#Getting-Started) -* [API](#API) - * [Shared Types](#Shared-Types) - * [Y.Doc](#YDoc) - * [Document Updates](#Document-Updates) - * [Relative Positions](#Relative-Positions) - * [Y.UndoManager](#YUndoManager) -* [Yjs CRDT Algorithm](#Yjs-CRDT-Algorithm) -* [License and Author](#License-and-Author) +* [Overview](#overview) + * [Bindings](#bindings) + * [Providers](#providers) + * [Ports](#ports) +* [Getting Started](#getting-started) +* [API](#api) + * [Shared Types](#shared-types) + * [Y.Doc](#ydoc) + * [Document Updates](#document-updates) + * [Relative Positions](#relative-positions) + * [Y.UndoManager](#yundomanager) +* [Yjs CRDT Algorithm](#yjs-crdt-algorithm) +* [License and Author](#license-and-author) ## Overview @@ -860,7 +860,7 @@ doc1.getArray('myarray').insert(0, ['Hello doc2, you got this?']) doc2.getArray('myarray').get(0) // => 'Hello doc2, you got this?' ``` -Yjs internally maintains a [state vector](#State-Vector) that denotes the next +Yjs internally maintains a [state vector](#state-vector) that denotes the next expected clock from each client. In a different interpretation it holds the number of structs created by each client. When two clients sync, you can either exchange the complete document structure or only the differences by sending the From 5b4d2a6bcf63e26f0637f9e83b4202d5f535906a Mon Sep 17 00:00:00 2001 From: Julian Lehrhuber Date: Thu, 29 Aug 2024 14:24:29 +0200 Subject: [PATCH 177/362] Add QDAcity to `README.md` --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8c21c464a..e9f414e06 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [screen.garden](https://screen.garden) - Collaborative backend for PKM apps. * [NextCloud](https://nextcloud.com/) - Content Collaboration Platform * [keystatic](https://github.com/Thinkmill/keystatic) - git-based CMS +* [QDAcity](https://qdacity.com) - Collaborative qualitative data analysis platform ## Table of Contents From f2ff8b95367250b8d080db0c98940997de1bc986 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 30 Aug 2024 19:09:59 +0200 Subject: [PATCH 178/362] add kanbert as a user --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e9f414e06..775f61252 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [NextCloud](https://nextcloud.com/) - Content Collaboration Platform * [keystatic](https://github.com/Thinkmill/keystatic) - git-based CMS * [QDAcity](https://qdacity.com) - Collaborative qualitative data analysis platform +* [Kanbert](https://kanbert.com) - Project management software ## Table of Contents From 95e2bc44297af7390806b902784b80f804309b33 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 2 Sep 2024 18:54:19 +0200 Subject: [PATCH 179/362] add secsync --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 775f61252..c21804ea7 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,11 @@ The reactive data store for local-first apps. They support multiple CRDTs and
Provider for sharing data in webxdc chat apps.
+
secsync
+
+An architecture to relay end-to-end encrypted CRDTs over a central service. +
+
#### Persistence Providers From 7422b18e87cb41ac675c17ea09dfa832253b6cd2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 4 Sep 2024 00:02:19 +0200 Subject: [PATCH 180/362] add eclipse theia as a user --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c21804ea7..844c9159f 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,8 @@ Showcase](https://yjs-diagram.synergy.codes/). * [keystatic](https://github.com/Thinkmill/keystatic) - git-based CMS * [QDAcity](https://qdacity.com) - Collaborative qualitative data analysis platform * [Kanbert](https://kanbert.com) - Project management software +* [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop + IDE that runs in the browser. ## Table of Contents From c1ef9a12b95619e6a148f41754c2b99847be50c4 Mon Sep 17 00:00:00 2001 From: Batchor Date: Thu, 5 Sep 2024 15:22:40 -0700 Subject: [PATCH 181/362] add ScienHub as a user. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 844c9159f..aae97bc91 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Kanbert](https://kanbert.com) - Project management software * [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop IDE that runs in the browser. +* [ScienHub](https://scienhub.com) - An AI-empowered scientific collaboration platform. ## Table of Contents From 4fb7789cdd8ccc1c535e850f99d5e4b91849a7cd Mon Sep 17 00:00:00 2001 From: Batchor Date: Thu, 5 Sep 2024 15:23:58 -0700 Subject: [PATCH 182/362] add ScienHub as a user. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aae97bc91..7da8f8491 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Kanbert](https://kanbert.com) - Project management software * [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop IDE that runs in the browser. -* [ScienHub](https://scienhub.com) - An AI-empowered scientific collaboration platform. +* [ScienHub](https://scienhub.com) - Collaborative LaTeX editor in the browser. ## Table of Contents From f604250fc3af79fe1c6fb40a5e309e2f67719f10 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 10 Sep 2024 15:35:46 +0200 Subject: [PATCH 183/362] add `ydoc.isDestroyed` property --- src/utils/Doc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 62643617c..d5165426f 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -104,6 +104,7 @@ export class Doc extends ObservableV2 { * lost (with false as a parameter). */ this.isSynced = false + this.isDestroyed = false /** * Promise that resolves once the document has been loaded from a presistence provider. */ @@ -322,6 +323,7 @@ export class Doc extends ObservableV2 { * Emit `destroy` event and unregister all event handlers. */ destroy () { + this.isDestroyed = true array.from(this.subdocs).forEach(subdoc => subdoc.destroy()) const item = this._item if (item !== null) { From 9a993f81d43690a9e25e54735dff70769371ff2b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 10 Sep 2024 15:37:58 +0200 Subject: [PATCH 184/362] 13.6.19 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e01df3321..c0757f88d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.18", + "version": "13.6.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.18", + "version": "13.6.19", "license": "MIT", "dependencies": { "lib0": "^0.2.86" diff --git a/package.json b/package.json index 68bb42b48..3452ac167 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.18", + "version": "13.6.19", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 8cd1a482bbcfb34c1a557afc7a15cdbb1816b7fd Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 26 Sep 2024 19:30:25 +0200 Subject: [PATCH 185/362] Y.Array.length should be 0 before it is integrated - #666 --- src/types/YArray.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/YArray.js b/src/types/YArray.js index 38b4e11f5..c70f2cdad 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -104,7 +104,7 @@ export class YArray extends AbstractType { } get length () { - return this._prelimContent === null ? this._length : this._prelimContent.length + return this._length } /** From 3bf44b98505abadd8b73db7492d2079484c38733 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 4 Oct 2024 21:07:19 +0200 Subject: [PATCH 186/362] #667 - add sanity messages when data is read before type is added to a document. --- src/types/AbstractType.js | 21 +++++++++++++++++++-- src/types/YArray.js | 2 ++ src/types/YMap.js | 11 +++++++---- src/types/YText.js | 4 ++++ src/types/YXmlFragment.js | 3 +++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 3dff240c2..24fa88023 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -17,6 +17,12 @@ import * as map from 'lib0/map' import * as iterator from 'lib0/iterator' import * as error from 'lib0/error' import * as math from 'lib0/math' +import * as log from 'lib0/logging' + +/** + * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats + */ +export const warnPrematureAccess = () => { log.warn('Invalid access: Add Yjs type to a document before reading data.') } const maxSearchMarker = 80 @@ -215,6 +221,7 @@ export const updateMarkerChanges = (searchMarker, index, len) => { * @return {Array} */ export const getTypeChildren = t => { + t.doc ?? warnPrematureAccess() let s = t._start const arr = [] while (s) { @@ -408,6 +415,7 @@ export class AbstractType { * @function */ export const typeListSlice = (type, start, end) => { + type.doc ?? warnPrematureAccess() if (start < 0) { start = type._length + start } @@ -443,6 +451,7 @@ export const typeListSlice = (type, start, end) => { * @function */ export const typeListToArray = type => { + type.doc ?? warnPrematureAccess() const cs = [] let n = type._start while (n !== null) { @@ -492,6 +501,7 @@ export const typeListToArraySnapshot = (type, snapshot) => { export const typeListForEach = (type, f) => { let index = 0 let n = type._start + type.doc ?? warnPrematureAccess() while (n !== null) { if (n.countable && !n.deleted) { const c = n.content.getContent() @@ -606,6 +616,7 @@ export const typeListForEachSnapshot = (type, f, snapshot) => { * @function */ export const typeListGet = (type, index) => { + type.doc ?? warnPrematureAccess() const marker = findMarker(type, index) let n = type._start if (marker !== null) { @@ -874,6 +885,7 @@ export const typeMapSet = (transaction, parent, key, value) => { * @function */ export const typeMapGet = (parent, key) => { + parent.doc ?? warnPrematureAccess() const val = parent._map.get(key) return val !== undefined && !val.deleted ? val.content.getContent()[val.length - 1] : undefined } @@ -890,6 +902,7 @@ export const typeMapGetAll = (parent) => { * @type {Object} */ const res = {} + parent.doc ?? warnPrematureAccess() parent._map.forEach((value, key) => { if (!value.deleted) { res[key] = value.content.getContent()[value.length - 1] @@ -907,6 +920,7 @@ export const typeMapGetAll = (parent) => { * @function */ export const typeMapHas = (parent, key) => { + parent.doc ?? warnPrematureAccess() const val = parent._map.get(key) return val !== undefined && !val.deleted } @@ -957,10 +971,13 @@ export const typeMapGetAllSnapshot = (parent, snapshot) => { } /** - * @param {Map} map + * @param {AbstractType & { _map: Map }} type * @return {IterableIterator>} * * @private * @function */ -export const createMapIterator = map => iterator.iteratorFilter(map.entries(), /** @param {any} entry */ entry => !entry[1].deleted) +export const createMapIterator = type => { + type.doc ?? warnPrematureAccess() + return iterator.iteratorFilter(type._map.entries(), /** @param {any} entry */ entry => !entry[1].deleted) +} diff --git a/src/types/YArray.js b/src/types/YArray.js index c70f2cdad..8fd5c215a 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -16,6 +16,7 @@ import { YArrayRefID, callTypeObservers, transact, + warnPrematureAccess, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' import { typeListSlice } from './AbstractType.js' @@ -104,6 +105,7 @@ export class YArray extends AbstractType { } get length () { + this.doc ?? warnPrematureAccess() return this._length } diff --git a/src/types/YMap.js b/src/types/YMap.js index 974e73164..22b94afb7 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -13,6 +13,7 @@ import { YMapRefID, callTypeObservers, transact, + warnPrematureAccess, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' @@ -121,6 +122,7 @@ export class YMap extends AbstractType { * @return {Object} */ toJSON () { + this.doc ?? warnPrematureAccess() /** * @type {Object} */ @@ -140,7 +142,7 @@ export class YMap extends AbstractType { * @return {number} */ get size () { - return [...createMapIterator(this._map)].length + return [...createMapIterator(this)].length } /** @@ -149,7 +151,7 @@ export class YMap extends AbstractType { * @return {IterableIterator} */ keys () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[0]) + return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[0]) } /** @@ -158,7 +160,7 @@ export class YMap extends AbstractType { * @return {IterableIterator} */ values () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1]) + return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1]) } /** @@ -167,7 +169,7 @@ export class YMap extends AbstractType { * @return {IterableIterator<[string, MapType]>} */ entries () { - return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]])) + return iterator.iteratorMap(createMapIterator(this), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]])) } /** @@ -176,6 +178,7 @@ export class YMap extends AbstractType { * @param {function(MapType,string,YMap):void} f A function to execute on every element of this YArray. */ forEach (f) { + this.doc ?? warnPrematureAccess() this._map.forEach((item, key) => { if (!item.deleted) { f(item.content.getContent()[item.length - 1], key, this) diff --git a/src/types/YText.js b/src/types/YText.js index 8919b009f..d4c59f038 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -26,6 +26,7 @@ import { typeMapGetAll, updateMarkerChanges, ContentType, + warnPrematureAccess, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line } from '../internals.js' @@ -875,6 +876,7 @@ export class YText extends AbstractType { * @type {number} */ get length () { + this.doc ?? warnPrematureAccess() return this._length } @@ -931,6 +933,7 @@ export class YText extends AbstractType { * @public */ toString () { + this.doc ?? warnPrematureAccess() let str = '' /** * @type {Item|null} @@ -1004,6 +1007,7 @@ export class YText extends AbstractType { * @public */ toDelta (snapshot, prevSnapshot, computeYChange) { + this.doc ?? warnPrematureAccess() /** * @type{Array} */ diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 1445139cd..2c0e9c5b0 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -17,6 +17,7 @@ import { transact, typeListGet, typeListSlice, + warnPrematureAccess, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook // eslint-disable-line } from '../internals.js' @@ -66,6 +67,7 @@ export class YXmlTreeWalker { */ this._currentNode = /** @type {Item} */ (root._start) this._firstCall = true + root.doc ?? warnPrematureAccess() } [Symbol.iterator] () { @@ -177,6 +179,7 @@ export class YXmlFragment extends AbstractType { } get length () { + this.doc ?? warnPrematureAccess() return this._prelimContent === null ? this._length : this._prelimContent.length } From 8152cf81cb6f11f96339450e2b0b061fed875063 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 4 Oct 2024 21:23:59 +0200 Subject: [PATCH 187/362] [#667] sanity checks for Yjs caveats. In dev_mode, objects inserted into Yjs can't be manipulated. --- package-lock.json | 9 +++++---- package.json | 4 ++-- src/structs/ContentAny.js | 6 ++++++ tests/y-array.tests.js | 25 +++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c0757f88d..69feaaa83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.6.19", "license": "MIT", "dependencies": { - "lib0": "^0.2.86" + "lib0": "^0.2.98" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", @@ -2785,13 +2785,14 @@ } }, "node_modules/lib0": { - "version": "0.2.88", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.88.tgz", - "integrity": "sha512-KyroiEvCeZcZEMx5Ys+b4u4eEBbA1ch7XUaBhYpwa/nPMrzTjUhI4RfcytmQfYoTBPcdyx+FX6WFNIoNuJzJfQ==", + "version": "0.2.98", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.98.tgz", + "integrity": "sha512-XteTiNO0qEXqqweWx+b21p/fBnNHUA1NwAtJNJek1oPrewEZs2uiT4gWivHKr9GqCjDPAhchz0UQO8NwU3bBNA==", "dependencies": { "isomorphic.js": "^0.2.4" }, "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", "0gentesthtml": "bin/gentesthtml.js", "0serve": "bin/0serve.js" }, diff --git a/package.json b/package.json index 3452ac167..4afa4fab0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "scripts": { "clean": "rm -rf dist docs", - "test": "npm run dist && node ./dist/tests.cjs --repetition-time 50", + "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", "dist": "npm run clean && rollup -c && tsc", "watch": "rollup -wc", @@ -76,7 +76,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.86" + "lib0": "^0.2.98" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", diff --git a/src/structs/ContentAny.js b/src/structs/ContentAny.js index 613144d82..3ab2dc5ff 100644 --- a/src/structs/ContentAny.js +++ b/src/structs/ContentAny.js @@ -2,6 +2,11 @@ import { UpdateEncoderV1, UpdateEncoderV2, UpdateDecoderV1, UpdateDecoderV2, Transaction, Item, StructStore // eslint-disable-line } from '../internals.js' +import * as env from 'lib0/environment' +import * as object from 'lib0/object' + +const isDevMode = env.getVariable('node_env') === 'development' + export class ContentAny { /** * @param {Array} arr @@ -11,6 +16,7 @@ export class ContentAny { * @type {Array} */ this.arr = arr + isDevMode && object.deepFreeze(arr) } /** diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 9875e85e2..1f593485b 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -4,6 +4,9 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' +import * as env from 'lib0/environment' + +const isDevMode = env.getVariable('node_env') === 'development' /** * @param {t.TestCase} tc @@ -17,6 +20,28 @@ export const testBasicUpdate = tc => { t.compare(doc2.getArray('array').toArray(), ['hi']) } +/** + * @param {t.TestCase} tc + */ +export const testFailsObjectManipulationInDevMode = tc => { + if (isDevMode) { + t.info('running in dev mode') + const doc = new Y.Doc() + const a = [1, 2, 3] + const b = { o: 1 } + doc.getArray('test').insert(0, [a]) + doc.getMap('map').set('k', b) + t.fails(() => { + a[0] = 42 + }) + t.fails(() => { + b.o = 42 + }) + } else { + t.info('not in dev mode') + } +} + /** * @param {t.TestCase} tc */ From 4ff65b5dc34a4054731c16d0a932e4f0dd9a94aa Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 7 Oct 2024 09:43:13 +0200 Subject: [PATCH 188/362] add devtools --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7da8f8491..4ced57acc 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Overview](#overview) * [Bindings](#bindings) * [Providers](#providers) + * [Tooling][#tooling] * [Ports](#ports) * [Getting Started](#getting-started) * [API](#api) @@ -294,7 +295,12 @@ A database and connection provider for Yjs based on Firestore.
-# Ports +### Tooling + +- [y-sweet debugger](https://docs.jamsocket.com/y-sweet/advanced/debugger) +- [liveblocks devtools](https://liveblocks.io/devtools) + +### Ports There are several Yjs-compatible ports to other programming languages. From 345fd31b102ef1beb980abf117670ca8bde2cc81 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 7 Oct 2024 09:45:27 +0200 Subject: [PATCH 189/362] add yjs-inspector --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4ced57acc..ef9a8670c 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,7 @@ A database and connection provider for Yjs based on Firestore. - [y-sweet debugger](https://docs.jamsocket.com/y-sweet/advanced/debugger) - [liveblocks devtools](https://liveblocks.io/devtools) +- [Yjs inspector](https://inspector.yjs.dev) ### Ports From 487465d701fd7c1e59f48d49f0cd081b79cd7379 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 14 Oct 2024 01:39:15 +0200 Subject: [PATCH 190/362] lint --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ef9a8670c..dcd363301 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Overview](#overview) * [Bindings](#bindings) * [Providers](#providers) - * [Tooling][#tooling] + * [Tooling](#tooling) * [Ports](#ports) * [Getting Started](#getting-started) * [API](#api) @@ -297,9 +297,9 @@ A database and connection provider for Yjs based on Firestore. ### Tooling -- [y-sweet debugger](https://docs.jamsocket.com/y-sweet/advanced/debugger) -- [liveblocks devtools](https://liveblocks.io/devtools) -- [Yjs inspector](https://inspector.yjs.dev) +* [y-sweet debugger](https://docs.jamsocket.com/y-sweet/advanced/debugger) +* [liveblocks devtools](https://liveblocks.io/devtools) +* [Yjs inspector](https://inspector.yjs.dev) ### Ports From a304024a76311e048bd0e57e5131c1d10e234d9f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 14 Oct 2024 01:41:22 +0200 Subject: [PATCH 191/362] 13.6.20 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69feaaa83..b29c699af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.19", + "version": "13.6.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.19", + "version": "13.6.20", "license": "MIT", "dependencies": { "lib0": "^0.2.98" diff --git a/package.json b/package.json index 4afa4fab0..4beb88f25 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.19", + "version": "13.6.20", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From e804dd757385d961fb5d6316c05f945ce0eac588 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 4 Sep 2024 16:55:46 +0200 Subject: [PATCH 192/362] add y-crdt elexir bindings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dcd363301..8527491fa 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,7 @@ language bindings to other languages * [yswift](https://github.com/y-crdt/yswift) - Swift binding * [yffi](https://github.com/y-crdt/y-crdt/tree/main/yffi) - C-FFI * [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) - WASM binding + * [y_ex](https://github.com/satoren/y_ex) - Elixir bindings * [ycs](https://github.com/yjs/ycs) - .Net compatible C# implementation. ## Getting Started From cc2d7320aa8736b55114853fee3a669a14d23c8d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Oct 2024 04:39:37 +0200 Subject: [PATCH 193/362] add funding.json --- funding.json | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 funding.json diff --git a/funding.json b/funding.json new file mode 100644 index 000000000..dbb1eca67 --- /dev/null +++ b/funding.json @@ -0,0 +1,97 @@ +{ + "version": "v1.0.0", + "entity": { + "type": "group", // Required. [individual, group, organisation, other]. Use the closest approximation. + "role": "steward", // Required. [owner, steward, maintainer, contributor, other]. Use the closest approximation. + "name": "Kevin Jahns", // Required. Name of the entity. Max len 250. + "email": "kevin.jahns@protonmail.com", // Required. Max len 250. + "description": "OSS Developer", // Required. Information about the entity. Max len 2000. + "webpageUrl": { + "url": "https://github.com/dmonad", // Required. Webpage with information about the entity. Starts with https:// or http://. Max len 250. + } + }, + + // Optional. One or more projects for which the funding is solicited. + "projects": [{ + "guid": "yjs", // Required. A short unique ID for the project. Lowercase-alphanumeric-dashes only. eg: my-cool-project. Max len 32. + "name": "Yjs", // Required. Name of the project. Max len 250. + "description": "A library for building collaborative applications. #p2p #local-first #CRDT Funding this project will also enable me to maintain the other Yjs-related technologies.", // Required. Description of the project. Max len 2000. + "webpageUrl": { + "url": "https://github.com/yjs/yjs", // Required. Webpage with information about the project. Starts with https:// or http://. Max len 250. + }, + "repositoryUrl": { + "url": "https://github.com/yjs/yjs", // Required. URL of the repository where the project's source code and other assets are available. Starts with https:// or http://. Max len 250. + "wellKnown": "" // Optional. Required if the above url and the URL of the funding.json manifest do not have the same hostname. Starts with https:// or http://. Max len 250. + }, + "licenses": ["spdx:MIT"], // Required. The project's licenses (up to 5). For standard licenses, use the license ID from the SDPX index prefixed by "spdx:". eg: "spdx:GPL-3.0", "spdx:CC-BY-SA-4.0" + "tags": ["collaboration", "p2p", "CRDT", "rich-text", "real-time"] // Required. Up to 10 general tags describing the project. Lowercase-alphanumeric-dashes (max 32 chars). eg: ["programming", "developer-tools"]. For reference, see tags.txt + }, { + "guid": "ystream", // Required. A short unique ID for the project. Lowercase-alphanumeric-dashes only. eg: my-cool-project. Max len 32. + "name": "Y/Stream", // Required. Name of the project. Max len 250. + "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.", // Required. Description of the project. Max len 2000. + "webpageUrl": { + "url": "https://github.com/yjs/ystream", // Required. Webpage with information about the project. Starts with https:// or http://. Max len 250. + }, + "repositoryUrl": { + "url": "https://github.com/yjs/ystream", // Required. URL of the repository where the project's source code and other assets are available. Starts with https:// or http://. Max len 250. + }, + "licenses": ["spdx:MIT", "spdx:GPL"], // Required. The project's licenses (up to 5). For standard licenses, use the license ID from the SDPX index prefixed by "spdx:". eg: "spdx:GPL-3.0", "spdx:CC-BY-SA-4.0" + "tags": ["collaboration", "p2p", "CRDT", "rich-text", "real-time"] // Required. Up to 10 general tags describing the project. Lowercase-alphanumeric-dashes (max 32 chars). eg: ["programming", "developer-tools"]. For reference, see tags.txt + }], + + // Required. + "funding": { + // Required. This describes one or more channels via which the entity can receive funds. + "channels": [{ + "guid": "github-sponsors", // Required. A short unique ID for the channel. Lowercase-alphanumeric-dashes only. eg: mybank, my-paypal. Max len 32. + "type": "gateway", // Required. [bank, gateway, cheque, cash, other]. + "address": "", // Optional. A short unstructured textual representation of the payment address for the channel. eg: "Account: 12345 (branch: ABCX)", "mypaypal@domain.com", "https://payment-url.com", or a physical address for cheques. Max len 250. + "description": "For funding of the Yjs project" // Optional. Any additional description or instructions for the payment channel. Max len 500. + }, { + "guid": "y-collective", // Required. A short unique ID for the channel. Lowercase-alphanumeric-dashes only. eg: mybank, my-paypal. Max len 32. + "type": "gateway", // Required. [bank, gateway, cheque, cash, other]. + "address": "https://opencollective.com/y-collective", // Optional. A short unstructured textual representation of the payment address for the channel. eg: "Account: 12345 (branch: ABCX)", "mypaypal@domain.com", "https://payment-url.com", or a physical address for cheques. Max len 250. + "description": "For funding of the Y-CRDT - the Rust implementation of Yjs" // Optional. Any additional description or instructions for the payment channel. Max len 500. + }], + + // Required. One or more funding and payment plans. + "plans": [{ + "guid": "supporter", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. + "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. + "name": "Supporter", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". + "description": "", // Optional. Any additional description or instructions for the funding plan. + "amount": 0, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". + "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD + "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] + "channels": ["github-sponsors", "y-collective"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. + }, { + "guid": "bronze-sponsor", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. + "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. + "name": "Bronze Sponsor", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". + "description": "This is the recommended plan for companies that use Yjs.", // Optional. Any additional description or instructions for the funding plan. + "amount": 500, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". + "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD + "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] + "channels": ["github-sponsors"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. + }, { + "guid": "silver-sponsor", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. + "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. + "name": "Silver Sponsor", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". + "description": "This is the recommended plan for large companies that use Yjs.", // Optional. Any additional description or instructions for the funding plan. + "amount": 1000, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". + "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD + "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] + "channels": ["github-sponsors"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. + }, { + "guid": "gold-sponsor", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. + "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. + "name": "Gold Sponsor", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". + "description": "This is the recommended plan for successfull companies that build their entire product around Yjs-related technologies.", // Optional. Any additional description or instructions for the funding plan. + "amount": 3000, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". + "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD + "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] + "channels": ["github-sponsors"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. + }], + } +} + From 42bbb44bfc168b339e67e2164df8d468ceb2b77b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Oct 2024 04:50:46 +0200 Subject: [PATCH 194/362] fix errors in funding.json --- funding.json | 226 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 133 insertions(+), 93 deletions(-) diff --git a/funding.json b/funding.json index dbb1eca67..8cdbe627f 100644 --- a/funding.json +++ b/funding.json @@ -1,97 +1,137 @@ { - "version": "v1.0.0", - "entity": { - "type": "group", // Required. [individual, group, organisation, other]. Use the closest approximation. - "role": "steward", // Required. [owner, steward, maintainer, contributor, other]. Use the closest approximation. - "name": "Kevin Jahns", // Required. Name of the entity. Max len 250. - "email": "kevin.jahns@protonmail.com", // Required. Max len 250. - "description": "OSS Developer", // Required. Information about the entity. Max len 2000. - "webpageUrl": { - "url": "https://github.com/dmonad", // Required. Webpage with information about the entity. Starts with https:// or http://. Max len 250. - } + "id": 0, + "guid": "", + "version": "", + "url": "", + "meta": {}, + "status": "", + "status_message": null, + "crawl_errors": 0, + "crawl_message": null, + "created_at": "0001-01-01T00:00:00Z", + "updated_at": "0001-01-01T00:00:00Z", + "entity": { + "type": "group", + "role": "steward", + "name": "Kevin Jahns", + "email": "kevin.jahns@protonmail.com", + "phone": "", + "description": "OSS Developer", + "webpageUrl": { + "url": "https://github.com/dmonad" + } + }, + "projects": [ + { + "guid": "yjs", + "name": "Yjs", + "description": "A library for building collaborative applications. #p2p #local-first #CRDT Funding this project will also enable me to maintain the other Yjs-related technologies.", + "webpageUrl": { + "url": "https://github.com/yjs/yjs" + }, + "repositoryUrl": { + "url": "https://github.com/yjs/yjs" + }, + "licenses": [ + "spdx:MIT" + ], + "tags": [ + "collaboration", + "p2p", + "CRDT", + "rich-text", + "real-time" + ] }, - - // Optional. One or more projects for which the funding is solicited. - "projects": [{ - "guid": "yjs", // Required. A short unique ID for the project. Lowercase-alphanumeric-dashes only. eg: my-cool-project. Max len 32. - "name": "Yjs", // Required. Name of the project. Max len 250. - "description": "A library for building collaborative applications. #p2p #local-first #CRDT Funding this project will also enable me to maintain the other Yjs-related technologies.", // Required. Description of the project. Max len 2000. - "webpageUrl": { - "url": "https://github.com/yjs/yjs", // Required. Webpage with information about the project. Starts with https:// or http://. Max len 250. - }, - "repositoryUrl": { - "url": "https://github.com/yjs/yjs", // Required. URL of the repository where the project's source code and other assets are available. Starts with https:// or http://. Max len 250. - "wellKnown": "" // Optional. Required if the above url and the URL of the funding.json manifest do not have the same hostname. Starts with https:// or http://. Max len 250. - }, - "licenses": ["spdx:MIT"], // Required. The project's licenses (up to 5). For standard licenses, use the license ID from the SDPX index prefixed by "spdx:". eg: "spdx:GPL-3.0", "spdx:CC-BY-SA-4.0" - "tags": ["collaboration", "p2p", "CRDT", "rich-text", "real-time"] // Required. Up to 10 general tags describing the project. Lowercase-alphanumeric-dashes (max 32 chars). eg: ["programming", "developer-tools"]. For reference, see tags.txt - }, { - "guid": "ystream", // Required. A short unique ID for the project. Lowercase-alphanumeric-dashes only. eg: my-cool-project. Max len 32. - "name": "Y/Stream", // Required. Name of the project. Max len 250. - "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.", // Required. Description of the project. Max len 2000. - "webpageUrl": { - "url": "https://github.com/yjs/ystream", // Required. Webpage with information about the project. Starts with https:// or http://. Max len 250. - }, - "repositoryUrl": { - "url": "https://github.com/yjs/ystream", // Required. URL of the repository where the project's source code and other assets are available. Starts with https:// or http://. Max len 250. - }, - "licenses": ["spdx:MIT", "spdx:GPL"], // Required. The project's licenses (up to 5). For standard licenses, use the license ID from the SDPX index prefixed by "spdx:". eg: "spdx:GPL-3.0", "spdx:CC-BY-SA-4.0" - "tags": ["collaboration", "p2p", "CRDT", "rich-text", "real-time"] // Required. Up to 10 general tags describing the project. Lowercase-alphanumeric-dashes (max 32 chars). eg: ["programming", "developer-tools"]. For reference, see tags.txt - }], - - // Required. - "funding": { - // Required. This describes one or more channels via which the entity can receive funds. - "channels": [{ - "guid": "github-sponsors", // Required. A short unique ID for the channel. Lowercase-alphanumeric-dashes only. eg: mybank, my-paypal. Max len 32. - "type": "gateway", // Required. [bank, gateway, cheque, cash, other]. - "address": "", // Optional. A short unstructured textual representation of the payment address for the channel. eg: "Account: 12345 (branch: ABCX)", "mypaypal@domain.com", "https://payment-url.com", or a physical address for cheques. Max len 250. - "description": "For funding of the Yjs project" // Optional. Any additional description or instructions for the payment channel. Max len 500. - }, { - "guid": "y-collective", // Required. A short unique ID for the channel. Lowercase-alphanumeric-dashes only. eg: mybank, my-paypal. Max len 32. - "type": "gateway", // Required. [bank, gateway, cheque, cash, other]. - "address": "https://opencollective.com/y-collective", // Optional. A short unstructured textual representation of the payment address for the channel. eg: "Account: 12345 (branch: ABCX)", "mypaypal@domain.com", "https://payment-url.com", or a physical address for cheques. Max len 250. - "description": "For funding of the Y-CRDT - the Rust implementation of Yjs" // Optional. Any additional description or instructions for the payment channel. Max len 500. - }], - - // Required. One or more funding and payment plans. - "plans": [{ - "guid": "supporter", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. - "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. - "name": "Supporter", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". - "description": "", // Optional. Any additional description or instructions for the funding plan. - "amount": 0, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". - "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD - "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] - "channels": ["github-sponsors", "y-collective"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. - }, { - "guid": "bronze-sponsor", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. - "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. - "name": "Bronze Sponsor", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". - "description": "This is the recommended plan for companies that use Yjs.", // Optional. Any additional description or instructions for the funding plan. - "amount": 500, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". - "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD - "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] - "channels": ["github-sponsors"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. - }, { - "guid": "silver-sponsor", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. - "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. - "name": "Silver Sponsor", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". - "description": "This is the recommended plan for large companies that use Yjs.", // Optional. Any additional description or instructions for the funding plan. - "amount": 1000, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". - "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD - "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] - "channels": ["github-sponsors"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. - }, { - "guid": "gold-sponsor", // Required. A short unique ID for the plan. Lowercase-alphanumeric-dashes only. eg: mybank, paypal. Max len 32. - "status": "active", // Required. [active, inactive]. Indicates whether this plan is currently active or inactive. - "name": "Gold Sponsor", // Required. Name of the funding plan. eg: "Starter support plan", "Infra hosting", "Monthly funding plan". - "description": "This is the recommended plan for successfull companies that build their entire product around Yjs-related technologies.", // Optional. Any additional description or instructions for the funding plan. - "amount": 3000, // Required. The solicited amount for this plan. 0 is a wildcard that indicates "any amount". - "currency": "USD", // Required. Three letter ISO 4217 currency code. eg: USD - "frequency": "monthly", // Required. [one-time, weekly, fortnightly, monthly, yearly, other] - "channels": ["github-sponsors"] // Required. One or more channel IDs defined in channels[] via which this plan can accept payments. - }], + { + "guid": "ystream", + "name": "Y/Stream", + "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" + }, + "repositoryUrl": { + "url": "https://github.com/yjs/ystream" + }, + "licenses": [ + "spdx:MIT", + "spdx:GPL-3.0" + ], + "tags": [ + "collaboration", + "p2p", + "CRDT", + "rich-text", + "real-time" + ] } + ], + "funding": { + "channels": [ + { + "guid": "github-sponsors", + "type": "payment-provider", + "address": "", + "description": "For funding of the Yjs project" + }, + { + "guid": "y-collective", + "type": "payment-provider", + "address": "https://opencollective.com/y-collective", + "description": "For funding of the Y-CRDT - the Rust implementation of Yjs" + } + ], + "plans": [ + { + "guid": "supporter", + "status": "active", + "name": "Supporter", + "description": "", + "amount": 0, + "currency": "USD", + "frequency": "monthly", + "channels": [ + "github-sponsors", + "y-collective" + ] + }, + { + "guid": "bronze-sponsor", + "status": "active", + "name": "Bronze Sponsor", + "description": "This is the recommended plan for companies that use Yjs.", + "amount": 500, + "currency": "USD", + "frequency": "monthly", + "channels": [ + "github-sponsors" + ] + }, + { + "guid": "silver-sponsor", + "status": "active", + "name": "Silver Sponsor", + "description": "This is the recommended plan for large companies that use Yjs.", + "amount": 1000, + "currency": "USD", + "frequency": "monthly", + "channels": [ + "github-sponsors" + ] + }, + { + "guid": "gold-sponsor", + "status": "active", + "name": "Gold Sponsor", + "description": "This is the recommended plan for successfull companies that build their entire product around Yjs-related technologies.", + "amount": 3000, + "currency": "USD", + "frequency": "monthly", + "channels": [ + "github-sponsors" + ] + } + ], + "history": null + } } - From 6074f80257675c8988495b77c501e33863029fb4 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Oct 2024 17:43:48 +0200 Subject: [PATCH 195/362] [funding.json] fix some validation issues --- funding.json | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/funding.json b/funding.json index 8cdbe627f..1a69263f1 100644 --- a/funding.json +++ b/funding.json @@ -1,15 +1,5 @@ { - "id": 0, - "guid": "", - "version": "", - "url": "", - "meta": {}, - "status": "", - "status_message": null, - "crawl_errors": 0, - "crawl_message": null, - "created_at": "0001-01-01T00:00:00Z", - "updated_at": "0001-01-01T00:00:00Z", + "version": "v1.0.0", "entity": { "type": "group", "role": "steward", @@ -18,7 +8,7 @@ "phone": "", "description": "OSS Developer", "webpageUrl": { - "url": "https://github.com/dmonad" + "url": "https://github.com/yjs" } }, "projects": [ @@ -48,10 +38,12 @@ "name": "Y/Stream", "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" + "url": "https://github.com/yjs/ystream", + "wellKnown": "https://github.com/yjs/ystream/blob/main/.well-known/funding-manifest-urls" }, "repositoryUrl": { - "url": "https://github.com/yjs/ystream" + "url": "https://github.com/yjs/ystream", + "wellKnown": "https://github.com/yjs/ystream/blob/main/.well-known/funding-manifest-urls" }, "licenses": [ "spdx:MIT", @@ -62,7 +54,8 @@ "p2p", "CRDT", "rich-text", - "real-time" + "real-time", + "web-development" ] } ], @@ -78,7 +71,7 @@ "guid": "y-collective", "type": "payment-provider", "address": "https://opencollective.com/y-collective", - "description": "For funding of the Y-CRDT - the Rust implementation of Yjs" + "description": "For funding the Y-CRDT - the Rust implementation of Yjs and other listed projects." } ], "plans": [ @@ -95,6 +88,18 @@ "y-collective" ] }, + { + "guid": "ystream-funding", + "status": "active", + "name": "YStream Funding", + "description": "Fund the next generation of local-first providers.", + "amount": 30000, + "currency": "USD", + "frequency": "one-time", + "channels": [ + "github-sponsors" + ] + }, { "guid": "bronze-sponsor", "status": "active", @@ -111,7 +116,7 @@ "guid": "silver-sponsor", "status": "active", "name": "Silver Sponsor", - "description": "This is the recommended plan for large companies that use Yjs.", + "description": "This is the recommended plan for large/successfull companies that use Yjs.", "amount": 1000, "currency": "USD", "frequency": "monthly", @@ -123,7 +128,7 @@ "guid": "gold-sponsor", "status": "active", "name": "Gold Sponsor", - "description": "This is the recommended plan for successfull companies that build their entire product around Yjs-related technologies.", + "description": "This is the recommended plan for successful companies that build their entire product around Yjs-related technologies.", "amount": 3000, "currency": "USD", "frequency": "monthly", From 4f47355893bbf9b6c9bee6d90081bc5b7ac10b16 Mon Sep 17 00:00:00 2001 From: Carlos Date: Tue, 22 Oct 2024 15:41:02 -0300 Subject: [PATCH 196/362] add SuperViz Provider in yjs README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 8527491fa..7e5c6033d 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,10 @@ documents. No configuration or maintenance is required. It also features Yjs webhook events, REST API to read and update Yjs documents, and a browser DevTools extension. +
@superviz/yjs
+
+ The SuperViz Yjs Provider comes with a secure, scalable real-time infrastructure for Yjs documents, fully compatible with a set of real-time collaboration components offered by SuperViz. This solution ensures synchronization, offline editing, and real-time updates, enabling multiple users to collaborate effectively within shared workspaces. +
y-sweet
A standalone yjs server with persistence to S3 or filesystem. They offer a From 43299973506f336dc4a0909109762f808a866158 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Oct 2024 18:05:42 +0200 Subject: [PATCH 197/362] add stars to providers that sponsor yjs --- README.md | 21 ++++++++++++++------- funding.json | 1 + 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7e5c6033d..3d179339c 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ are available. Communication over the signaling servers can be encrypted by providing a shared secret, keeping the connection information and the shared document private.
-
@liveblocks/yjs
+
@liveblocks/yjs 🌟
Liveblocks Yjs provides a fully hosted WebSocket infrastructure and persisted data store for Yjs @@ -199,19 +199,26 @@ documents. No configuration or maintenance is required. It also features Yjs webhook events, REST API to read and update Yjs documents, and a browser DevTools extension.
-
@superviz/yjs
-
- The SuperViz Yjs Provider comes with a secure, scalable real-time infrastructure for Yjs documents, fully compatible with a set of real-time collaboration components offered by SuperViz. This solution ensures synchronization, offline editing, and real-time updates, enabling multiple users to collaborate effectively within shared workspaces. -
-
y-sweet
+
y-sweet ⭐
A standalone yjs server with persistence to S3 or filesystem. They offer a cloud service as well.
-
Hocuspocus
+
Hocuspocus ⭐
A standalone extensible yjs server with sqlite persistence, webhooks, auth and more.
+
@superviz/yjs
+
+ The + SuperViz Yjs Provider + + comes with a secure, scalable real-time infrastructure for Yjs + documents, fully compatible with a set of real-time collaboration + components offered by SuperViz. This solution ensures synchronization, + offline editing, and real-time updates, enabling multiple users to + collaborate effectively within shared workspaces. +
PartyKit
Cloud service for building multiplayer apps. diff --git a/funding.json b/funding.json index 1a69263f1..5a6c33c80 100644 --- a/funding.json +++ b/funding.json @@ -50,6 +50,7 @@ "spdx:GPL-3.0" ], "tags": [ + "privacy", "collaboration", "p2p", "CRDT", From 8dc1296a0bd15e70c573160593c2617e0d772f01 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Oct 2024 18:07:52 +0200 Subject: [PATCH 198/362] update readme --- README.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3d179339c..c8079ccfd 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ are available. Communication over the signaling servers can be encrypted by providing a shared secret, keeping the connection information and the shared document private.
-
@liveblocks/yjs 🌟
+
@liveblocks/yjs 🌟
Liveblocks Yjs provides a fully hosted WebSocket infrastructure and persisted data store for Yjs @@ -199,25 +199,22 @@ documents. No configuration or maintenance is required. It also features Yjs webhook events, REST API to read and update Yjs documents, and a browser DevTools extension.
-
y-sweet ⭐
+
y-sweet
A standalone yjs server with persistence to S3 or filesystem. They offer a cloud service as well.
-
Hocuspocus ⭐
+
Hocuspocus
A standalone extensible yjs server with sqlite persistence, webhooks, auth and more.
-
@superviz/yjs
+
@superviz/yjs
- The - SuperViz Yjs Provider - - comes with a secure, scalable real-time infrastructure for Yjs - documents, fully compatible with a set of real-time collaboration - components offered by SuperViz. This solution ensures synchronization, - offline editing, and real-time updates, enabling multiple users to - collaborate effectively within shared workspaces. + SuperViz Yjs Provider comes with a secure, scalable real-time infrastructure + for Yjs documents, fully compatible with a set of real-time + collaboration components offered by SuperViz. This solution ensures + synchronization, offline editing, and real-time updates, enabling + multiple users to collaborate effectively within shared workspaces.
PartyKit
From 4e2d3c8ac6ddc1acc84962ce67fc8de122af34aa Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Wed, 27 Nov 2024 15:42:50 -0800 Subject: [PATCH 199/362] docs: remove `@toeverything/y-indexeddb` --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index c8079ccfd..f8bc3899f 100644 --- a/README.md +++ b/README.md @@ -280,11 +280,6 @@ network provider.
Adds persistent storage to a server with MongoDB. Can be used with the y-websocket provider. -
-
-@toeverything/y-indexeddb
-
-Like y-indexeddb, but with sub-documents support and fully TypeScript.
y-fire
From c951f2b7eae39bd338bfb0ea59851275f195218f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 28 Nov 2024 01:08:37 +0100 Subject: [PATCH 200/362] add Open Collaboration Tools as a user --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c8079ccfd..75d19de51 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,8 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop IDE that runs in the browser. * [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 ## Table of Contents From 4ffd3709f839588aa56f1ec438c23fe7a5fd956f Mon Sep 17 00:00:00 2001 From: Viet Hoang Do Date: Fri, 6 Dec 2024 09:58:19 +1000 Subject: [PATCH 201/362] Add PSPDFKit binding to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0041e8727..033e46665 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ are implemented in separate modules. | React | | [react-yjs](https://github.com/nikgraf/react-yjs) | [demo](https://react-yjs-example.vercel.app/) | | 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) | ### Providers From 4404d090e424a096a5e3f1977f4ff13000d95480 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 18 Dec 2024 14:34:26 +0100 Subject: [PATCH 202/362] add nodejs specific tests --- tests/index.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/index.js b/tests/index.js index ec22ed05b..fd9c08e79 100644 --- a/tests/index.js +++ b/tests/index.js @@ -15,15 +15,28 @@ import * as relativePositions from './relativePositions.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) } -runTests({ + +/** + * @type {any} + */ +const tests = { doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions -}).then(success => { +} + +const run = async () => { + if (environment.isNode) { + // tests.nodejs = await import('./node.tests.js') + } + + const success = await runTests(tests) /* istanbul ignore next */ if (isNode) { process.exit(success ? 0 : 1) } -}) +} +run() From 1b0f2e5463cf56d47bfbfc44b7752f5f1784b4fa Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 18 Dec 2024 14:35:13 +0100 Subject: [PATCH 203/362] lint --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 033e46665..5803373fc 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop IDE that runs in the browser. * [ScienHub](https://scienhub.com) - Collaborative LaTeX editor in the browser. -- [Open Collaboration Tools](https://www.open-collab.tools/) - Collaborative +* [Open Collaboration Tools](https://www.open-collab.tools/) - Collaborative editing for your IDE or custom editor ## Table of Contents From f583d2a211fedc94c568e0887b8bb6aed03dcd72 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 21 Dec 2024 00:52:48 +0100 Subject: [PATCH 204/362] fix #657 - relative positions issue when using followUndoneDeletions=false --- src/utils/RelativePosition.js | 16 ++++++++++++++-- tests/relativePositions.tests.js | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index 214fc6f0e..cba659962 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -9,7 +9,7 @@ import { ContentType, followRedone, getItem, - ID, Doc, AbstractType // eslint-disable-line + StructStore, ID, Doc, AbstractType, // eslint-disable-line } from '../internals.js' import * as encoding from 'lib0/encoding' @@ -256,6 +256,18 @@ export const readRelativePosition = decoder => { */ export const decodeRelativePosition = uint8Array => readRelativePosition(decoding.createDecoder(uint8Array)) +/** + * @param {StructStore} store + * @param {ID} id + */ +const getItemWithOffset = (store, id) => { + const item = getItem(store, id) + const diff = id.clock - item.id.clock + return { + item, diff + } +} + /** * Transform a relative position to an absolute position. * @@ -286,7 +298,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndo if (getState(store, rightID.client) <= rightID.clock) { return null } - const res = followUndoneDeletions ? followRedone(store, rightID) : { item: getItem(store, rightID), diff: 0 } + const res = followUndoneDeletions ? followRedone(store, rightID) : getItemWithOffset(store, rightID) const right = res.item if (!(right instanceof Item)) { return null diff --git a/tests/relativePositions.tests.js b/tests/relativePositions.tests.js index ab86168b5..75e7088d8 100644 --- a/tests/relativePositions.tests.js +++ b/tests/relativePositions.tests.js @@ -85,6 +85,26 @@ export const testRelativePositionCase6 = tc => { checkRelativePositions(ytext) } +/** + * Testing https://github.com/yjs/yjs/issues/657 + * + * @param {t.TestCase} tc + */ +export const testRelativePositionCase7 = tc => { + const docA = new Y.Doc() + const textA = docA.getText('text') + textA.insert(0, 'abcde') + // Create a relative position at index 2 in 'textA' + const relativePosition = Y.createRelativePositionFromTypeIndex(textA, 2) + // Verify that the absolutes positions on 'docA' are the same + const absolutePositionWithFollow = + Y.createAbsolutePositionFromRelativePosition(relativePosition, docA, true) + const absolutePositionWithoutFollow = + Y.createAbsolutePositionFromRelativePosition(relativePosition, docA, false) + t.assert(absolutePositionWithFollow?.index === 2) + t.assert(absolutePositionWithoutFollow?.index === 2) +} + /** * @param {t.TestCase} tc */ From 89dddc2a95079460b7203bde07f45dffd56629f2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 21 Dec 2024 00:55:05 +0100 Subject: [PATCH 205/362] 13.6.21 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b29c699af..23cc83435 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.20", + "version": "13.6.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.20", + "version": "13.6.21", "license": "MIT", "dependencies": { "lib0": "^0.2.98" diff --git a/package.json b/package.json index 4beb88f25..9304d9bb8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.20", + "version": "13.6.21", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From f18eab2dfed7593ebf23ace2e147f1cf2c0dc18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Fri, 3 Jan 2025 18:11:43 +0000 Subject: [PATCH 206/362] Fix typos --- INTERNALS.md | 2 +- README.md | 10 +++++----- src/structs/AbstractStruct.js | 2 +- src/types/AbstractType.js | 10 +++++----- src/types/YText.js | 4 ++-- src/types/YXmlEvent.js | 2 +- src/utils/Doc.js | 2 +- src/utils/PermanentUserData.js | 2 +- src/utils/RelativePosition.js | 2 +- src/utils/StructStore.js | 4 ++-- src/utils/UpdateEncoder.js | 2 +- src/utils/encoding.js | 14 +++++++------- tests/compatibility.tests.js | 2 +- tests/snapshot.tests.js | 2 +- tests/y-map.tests.js | 12 ++++++------ tests/y-text.tests.js | 2 +- 16 files changed, 37 insertions(+), 37 deletions(-) diff --git a/INTERNALS.md b/INTERNALS.md index 8ec81c077..79bd7f29e 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -60,7 +60,7 @@ characters have either been deleted or all characters are not deleted. The item will be split if the run is interrupted for any reason (eg a character in the middle of the run is deleted). -When an item is created, it stores a reference to the IDs of the preceeding and +When an item is created, it stores a reference to the IDs of the preceding and succeeding item. These are stored in the item's `origin` and `originRight` fields, respectively. These are used when peers concurrently insert at the same location in a document. Though quite rare in practice, Yjs needs to make sure diff --git a/README.md b/README.md index 5803373fc..20cab46da 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [Synthesia](https://www.synthesia.io) - Collaborative Video Editor * [thinkdeli](https://thinkdeli.com) - A fast and simple notes app powered by AI * [ourboard](https://github.com/raimohanska/ourboard) - A collaborative whiteboard - applicaiton + application * [Ellie.ai](https://ellie.ai) - Data Product Design and Collaboration * [GoPeer](https://gopeer.org/) - Collaborative tutoring * [screen.garden](https://screen.garden) - Collaborative backend for PKM apps. @@ -189,7 +189,7 @@ backends to y-websocket.
y-webrtc
Propagates document updates peer-to-peer using WebRTC. The peers exchange -signaling data over signaling servers. Publically available signaling servers +signaling data over signaling servers. Publicly 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. @@ -1097,7 +1097,7 @@ encoding format for document updates. If you prefer JSON encoding, you can simply JSON.stringify / JSON.parse the relative position instead.
Y.decodeRelativePosition(Uint8Array):RelativePosition -
Decode a binary-encoded relative position to a RelativePositon object.
+
Decode a binary-encoded relative position to a RelativePosition object.
### Y.UndoManager @@ -1277,11 +1277,11 @@ More information about the specific implementation is available in CRDTs that are suitable for shared text editing suffer from the fact that they only grow in size. There are CRDTs that do not grow in size, but they do not -have the characteristics that are benificial for shared text editing (like +have the characteristics that are beneficial for shared text editing (like intention preservation). Yjs implements many improvements to the original algorithm that diminish the trade-off that the document only grows in size. We can't garbage collect deleted structs (tombstones) while ensuring a unique -order of the structs. But we can 1. merge preceeding structs into a single +order of the structs. But we can 1. merge preceding structs into a single struct to reduce the amount of meta information, 2. we can delete content from the struct if it is deleted, and 3. we can garbage collect tombstones if we don't care about the order of the structs anymore (e.g. if the parent was diff --git a/src/structs/AbstractStruct.js b/src/structs/AbstractStruct.js index ad0000539..52773eb70 100644 --- a/src/structs/AbstractStruct.js +++ b/src/structs/AbstractStruct.js @@ -26,7 +26,7 @@ export class AbstractStruct { * This method is already assuming that `this.id.clock + this.length === this.id.clock`. * Also this method does *not* remove right from StructStore! * @param {AbstractStruct} right - * @return {boolean} wether this merged with right + * @return {boolean} whether this merged with right */ mergeWith (right) { return false diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 24fa88023..019369ecf 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -155,11 +155,11 @@ export const findMarker = (yarray, index) => { // } // } // if (marker) { - // if (window.lengthes == null) { - // window.lengthes = [] - // window.getLengthes = () => window.lengthes.sort((a, b) => a - b) + // if (window.lengths == null) { + // window.lengths = [] + // window.getLengths = () => window.lengths.sort((a, b) => a - b) // } - // window.lengthes.push(marker.index - pindex) + // window.lengths.push(marker.index - pindex) // console.log('distance', marker.index - pindex, 'len', p && p.parent.length) // } if (marker !== null && math.abs(marker.index - pindex) < /** @type {YText|YArray} */ (p.parent).length / maxSearchMarker) { @@ -751,7 +751,7 @@ export const typeListInsertGenerics = (transaction, parent, index, content) => { /** * Pushing content is special as we generally want to push after the last item. So we don't have to update - * the serach marker. + * the search marker. * * @param {Transaction} transaction * @param {AbstractType} parent diff --git a/src/types/YText.js b/src/types/YText.js index d4c59f038..a196ecd08 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -478,7 +478,7 @@ export const cleanupYTextFormatting = type => { } /** - * This will be called by the transction once the event handlers are called to potentially cleanup + * This will be called by the transaction once the event handlers are called to potentially cleanup * formatting attributes. * * @param {Transaction} transaction @@ -568,7 +568,7 @@ const deleteText = (transaction, currPos, length) => { /** * The Quill Delta format represents changes on a text document with - * formatting information. For mor information visit {@link https://quilljs.com/docs/delta/|Quill Delta} + * formatting information. For more information visit {@link https://quilljs.com/docs/delta/|Quill Delta} * * @example * { diff --git a/src/types/YXmlEvent.js b/src/types/YXmlEvent.js index 022b72d58..f18a06e89 100644 --- a/src/types/YXmlEvent.js +++ b/src/types/YXmlEvent.js @@ -12,7 +12,7 @@ export class YXmlEvent extends YEvent { * @param {YXmlElement|YXmlText|YXmlFragment} target The target on which the event is created. * @param {Set} subs The set of changed attributes. `null` is included if the * child list changed. - * @param {Transaction} transaction The transaction instance with wich the + * @param {Transaction} transaction The transaction instance with which the * change was created. */ constructor (target, subs, transaction) { diff --git a/src/utils/Doc.js b/src/utils/Doc.js index d5165426f..317f8c213 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -106,7 +106,7 @@ export class Doc extends ObservableV2 { this.isSynced = false this.isDestroyed = false /** - * Promise that resolves once the document has been loaded from a presistence provider. + * Promise that resolves once the document has been loaded from a persistence provider. */ this.whenLoaded = promise.create(resolve => { this.on('load', () => { diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js index 80b712590..a5d990b94 100644 --- a/src/utils/PermanentUserData.js +++ b/src/utils/PermanentUserData.js @@ -62,7 +62,7 @@ export class PermanentUserData { initUser(storeType.get(userDescription), userDescription) ) }) - // add intial data + // add initial data storeType.forEach(initUser) } diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index cba659962..4f6ced71e 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -66,7 +66,7 @@ export class RelativePosition { * after the meant position. * I.e. position 1 in 'ab' is associated to character 'b'. * - * If assoc < 0, then the relative position is associated to the caharacter + * If assoc < 0, then the relative position is associated to the character * before the meant position. * * @type {number} diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 55a851788..692743d7b 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -66,13 +66,13 @@ export const getState = (store, client) => { * @private * @function */ -export const integretyCheck = store => { +export const integrityCheck = store => { store.clients.forEach(structs => { for (let i = 1; i < structs.length; i++) { const l = structs[i - 1] const r = structs[i] if (l.id.clock + l.length !== r.id.clock) { - throw new Error('StructStore failed integrety check') + throw new Error('StructStore failed integrity check') } } }) diff --git a/src/utils/UpdateEncoder.js b/src/utils/UpdateEncoder.js index 8cf303819..2b742beea 100644 --- a/src/utils/UpdateEncoder.js +++ b/src/utils/UpdateEncoder.js @@ -167,7 +167,7 @@ export class UpdateEncoderV2 extends DSEncoderV2 { */ this.keyMap = new Map() /** - * Refers to the next uniqe key-identifier to me used. + * Refers to the next unique key-identifier to me used. * See writeKey method for more information. * * @type {number} diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 08a9602db..b195ccc3b 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -211,7 +211,7 @@ export const readClientsStructRefs = (decoder, doc) => { * then we start emptying the stack. * * It is not possible to have circles: i.e. struct1 (from client1) depends on struct2 (from client2) - * depends on struct3 (from client1). Therefore the max stack size is eqaul to `structReaders.length`. + * depends on struct3 (from client1). Therefore the max stack size is equal to `structReaders.length`. * * This method is implemented in a way so that we can resume computation if this update * causally depends on another update. @@ -279,14 +279,14 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { const addStackToRestSS = () => { for (const item of stack) { const client = item.id.client - const unapplicableItems = clientsStructRefs.get(client) - if (unapplicableItems) { + const inapplicableItems = clientsStructRefs.get(client) + if (inapplicableItems) { // decrement because we weren't able to apply previous operation - unapplicableItems.i-- - restStructs.clients.set(client, unapplicableItems.refs.slice(unapplicableItems.i)) + inapplicableItems.i-- + restStructs.clients.set(client, inapplicableItems.refs.slice(inapplicableItems.i)) clientsStructRefs.delete(client) - unapplicableItems.i = 0 - unapplicableItems.refs = [] + inapplicableItems.i = 0 + inapplicableItems.refs = [] } else { // item was the last item on clientsStructRefs and the field was already cleared. Add item to restStructs and continue restStructs.clients.set(client, [item]) diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index fc71364c2..0b8e11003 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -1,5 +1,5 @@ /** - * Testing if encoding/decoding compatibility and integration compatiblity is given. + * Testing if encoding/decoding compatibility and integration compatibility is given. * We expect that the document always looks the same, even if we upgrade the integration algorithm, or add additional encoding approaches. * * The v1 documents were generated with Yjs v13.2.0 based on the randomisized tests. diff --git a/tests/snapshot.tests.js b/tests/snapshot.tests.js index 4f3ecd47d..01c956f4f 100644 --- a/tests/snapshot.tests.js +++ b/tests/snapshot.tests.js @@ -58,7 +58,7 @@ export const testEmptyRestoreSnapshot = _tc => { t.compare(docRestored.getArray().toArray(), []) t.compare(doc.getArray().toArray(), ['world']) - // now this snapshot reflects the latest state. It shoult still work. + // now this snapshot reflects the latest state. It should still work. const snap2 = Y.snapshot(doc) const docRestored2 = Y.createDocFromSnapshot(doc, snap2) t.compare(docRestored2.getArray().toArray(), ['world']) diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 70b3e3b1b..080b71406 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -369,11 +369,11 @@ export const testObserversUsingObservedeep = tc => { /** * @type {Array>} */ - const pathes = [] + const paths = [] let calls = 0 map0.observeDeep(events => { events.forEach(event => { - pathes.push(event.path) + paths.push(event.path) }) calls++ }) @@ -381,7 +381,7 @@ export const testObserversUsingObservedeep = tc => { map0.get('map').set('array', new Y.Array()) map0.get('map').get('array').insert(0, ['content']) t.assert(calls === 3) - t.compare(pathes, [[], ['map'], ['map', 'array']]) + t.compare(paths, [[], ['map'], ['map', 'array']]) compare(users) } @@ -393,14 +393,14 @@ export const testPathsOfSiblingEvents = tc => { /** * @type {Array>} */ - const pathes = [] + const paths = [] let calls = 0 const doc = users[0] map0.set('map', new Y.Map()) map0.get('map').set('text1', new Y.Text('initial')) map0.observeDeep(events => { events.forEach(event => { - pathes.push(event.path) + paths.push(event.path) }) calls++ }) @@ -409,7 +409,7 @@ export const testPathsOfSiblingEvents = tc => { map0.get('map').set('text2', new Y.Text('new')) }) t.assert(calls === 1) - t.compare(pathes, [['map'], ['map', 'text1']]) + t.compare(paths, [['map'], ['map', 'text1']]) compare(users) } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 34ae7e9e7..4b9c6d2e9 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -376,7 +376,7 @@ export const testDeltaBug = _tc => { }, { insert: '\n', - // This attibutes has only list and no table-cell-line + // This attributes has only list and no table-cell-line attributes: { list: { rowspan: '1', From e67b1296a7bbde60d7a4d8ace511153aa0321fea Mon Sep 17 00:00:00 2001 From: Yuxiang Kou <97371951+ykou-clickup@users.noreply.github.com> Date: Thu, 9 Jan 2025 14:58:47 -0800 Subject: [PATCH 207/362] fix(yText): applyDelta should support both Delta and Ops[] Fixed an issue that the yText.applyDelta() accepted only Ops[], but not Delta. --- src/types/YText.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index a196ecd08..a754cde1b 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -972,15 +972,16 @@ export class YText extends AbstractType { if (this.doc !== null) { transact(this.doc, transaction => { const currPos = new ItemTextListPosition(null, this._start, 0, new Map()) - for (let i = 0; i < delta.length; i++) { - const op = delta[i] + const ops = Array.isArray(delta) ? delta : (Array.isArray(delta?.ops) ? delta.ops : []); + for (let i = 0; i < ops.length; i++) { + const op = ops[i] if (op.insert !== undefined) { // Quill assumes that the content starts with an empty paragraph. // Yjs/Y.Text assumes that it starts empty. We always hide that // there is a newline at the end of the content. // If we omit this step, clients will see a different number of // paragraphs, but nothing bad will happen. - const ins = (!sanitize && typeof op.insert === 'string' && i === delta.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert + const ins = (!sanitize && typeof op.insert === 'string' && i === ops.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert if (typeof ins !== 'string' || ins.length > 0) { insertText(transaction, this, currPos, ins, op.attributes || {}) } From 80e83a84c6d8c9a4ca1c447f9f94e23e690a9516 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 12 Jan 2025 19:32:51 +0100 Subject: [PATCH 208/362] Revert "fix(yText): applyDelta should support both Delta and Ops[]" --- src/types/YText.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index a754cde1b..a196ecd08 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -972,16 +972,15 @@ export class YText extends AbstractType { if (this.doc !== null) { transact(this.doc, transaction => { const currPos = new ItemTextListPosition(null, this._start, 0, new Map()) - const ops = Array.isArray(delta) ? delta : (Array.isArray(delta?.ops) ? delta.ops : []); - for (let i = 0; i < ops.length; i++) { - const op = ops[i] + for (let i = 0; i < delta.length; i++) { + const op = delta[i] if (op.insert !== undefined) { // Quill assumes that the content starts with an empty paragraph. // Yjs/Y.Text assumes that it starts empty. We always hide that // there is a newline at the end of the content. // If we omit this step, clients will see a different number of // paragraphs, but nothing bad will happen. - const ins = (!sanitize && typeof op.insert === 'string' && i === ops.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert + const ins = (!sanitize && typeof op.insert === 'string' && i === delta.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert if (typeof ins !== 'string' || ins.length > 0) { insertText(transaction, this, currPos, ins, op.attributes || {}) } From bb5410b6ddc74a23e7bdb75aa09d252aaed81b8d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 12 Jan 2025 19:41:19 +0100 Subject: [PATCH 209/362] marginally better typings for applyDelta - #689 --- package-lock.json | 8 ++++---- package.json | 2 +- src/types/YText.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23cc83435..998e5cc25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.6.21", "license": "MIT", "dependencies": { - "lib0": "^0.2.98" + "lib0": "^0.2.99" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", @@ -2785,9 +2785,9 @@ } }, "node_modules/lib0": { - "version": "0.2.98", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.98.tgz", - "integrity": "sha512-XteTiNO0qEXqqweWx+b21p/fBnNHUA1NwAtJNJek1oPrewEZs2uiT4gWivHKr9GqCjDPAhchz0UQO8NwU3bBNA==", + "version": "0.2.99", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.99.tgz", + "integrity": "sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==", "dependencies": { "isomorphic.js": "^0.2.4" }, diff --git a/package.json b/package.json index 9304d9bb8..41968c182 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.98" + "lib0": "^0.2.99" }, "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", diff --git a/src/types/YText.js b/src/types/YText.js index a196ecd08..75bf5dd64 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -961,7 +961,7 @@ export class YText extends AbstractType { /** * Apply a {@link Delta} on this shared YText type. * - * @param {any} delta The changes to apply on this element. + * @param {Array} delta The changes to apply on this element. * @param {object} opts * @param {boolean} [opts.sanitize] Sanitize input delta. Removes ending newlines if set to true. * From 1bec008862d8682e51ecc6d8a0f1a2c13f25c241 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 12 Jan 2025 19:45:03 +0100 Subject: [PATCH 210/362] 13.6.22 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 998e5cc25..44a8957ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.21", + "version": "13.6.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.21", + "version": "13.6.22", "license": "MIT", "dependencies": { "lib0": "^0.2.99" diff --git a/package.json b/package.json index 41968c182..88d3c4049 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.21", + "version": "13.6.22", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From e53c44e3a6d04013c876341514211603deb4d8c2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 15 Jan 2025 21:44:18 +0100 Subject: [PATCH 211/362] expose getItemCleanStart/End --- src/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.js b/src/index.js index b13b2077b..5382e376e 100644 --- a/src/index.js +++ b/src/index.js @@ -50,6 +50,8 @@ export { findRootTypeKey, findIndexSS, getItem, + getItemCleanStart, + getItemCleanEnd, typeListToArraySnapshot, typeMapGetSnapshot, typeMapGetAllSnapshot, From c05b815b4c6e680ffeefa737d46c2bf1f9f444b5 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 15 Jan 2025 21:46:23 +0100 Subject: [PATCH 212/362] 13.6.23 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 44a8957ca..ea3b8cf23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.22", + "version": "13.6.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.22", + "version": "13.6.23", "license": "MIT", "dependencies": { "lib0": "^0.2.99" diff --git a/package.json b/package.json index 88d3c4049..b362704d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.22", + "version": "13.6.23", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 40725e373b41b5115dd0b824d84764c30740cd57 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Fri, 17 Jan 2025 11:57:20 +0100 Subject: [PATCH 213/362] Add Typst to Yjs users in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 20cab46da..c7cbc2745 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,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 From cc9a8574416d803adf5fd645725b94e5db1b9ec5 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 24 Feb 2025 20:30:48 +0100 Subject: [PATCH 214/362] slightly optimize TreeWalker and integration process --- src/structs/Item.js | 3 +-- src/types/YXmlFragment.js | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/structs/Item.js b/src/structs/Item.js index 1b7ba9395..2d2b1bb92 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -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 } diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 2c0e9c5b0..544a18ceb 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -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 From 69d4a5c821123f2b08b69390ab9eaba40b200e84 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 4 Mar 2025 14:41:44 +0100 Subject: [PATCH 215/362] [UndoManager] support global undo --- src/utils/UndoManager.js | 24 +++++++++++++----------- tests/undo-redo.tests.js | 13 +++++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index a3645cd87..3cc395dec 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -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} */ (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} */ (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} */ (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|Array>} typeScope Accepts either a single type, or an array of types + * @param {Doc|AbstractType|Array>} typeScope Accepts either a single type, or 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>} + * @type {Array | 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} */ (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} */ (type), item))) { keepItem(item, true) } }) @@ -272,13 +272,15 @@ export class UndoManager extends ObservableV2 { } /** - * @param {Array> | AbstractType} ytypes + * @param {Array | Doc> | AbstractType | 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) } }) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 2fb2ff2e2..df9f9ab38 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -116,6 +116,19 @@ export const testEmptyTypeScope = _tc => { t.assert(yarray.length === 0) } +/** + * 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 From 78e0527b468b0aa57fbea31fbb0a4989f08641df Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 4 Mar 2025 14:44:19 +0100 Subject: [PATCH 216/362] 13.6.24 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea3b8cf23..31af45d88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.23", + "version": "13.6.24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.23", + "version": "13.6.24", "license": "MIT", "dependencies": { "lib0": "^0.2.99" diff --git a/package.json b/package.json index b362704d8..18854b84d 100644 --- a/package.json +++ b/package.json @@ -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", From 09fbb62ba9f28dca15c75c3a9804a6f0dd87ad1a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 4 Mar 2025 14:52:19 +0100 Subject: [PATCH 217/362] improve documentation on global UndoManager --- src/utils/UndoManager.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 3cc395dec..98410e4de 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -159,7 +159,7 @@ const popStackItem = (undoManager, stack, eventType) => { */ export class UndoManager extends ObservableV2 { /** - * @param {Doc|AbstractType|Array>} typeScope Accepts either a single type, or an array of types + * @param {Doc|AbstractType|Array>} 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, { @@ -272,6 +272,8 @@ export class UndoManager extends ObservableV2 { } /** + * Extend the scope. + * * @param {Array | Doc> | AbstractType | Doc} ytypes */ addToScope (ytypes) { From afa4c358663ae27bff734e98d5f431942992de48 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 5 Mar 2025 14:15:12 +0100 Subject: [PATCH 218/362] update titanic funding information - closes #696 --- funding.json | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/funding.json b/funding.json index 5a6c33c80..3ae1c2664 100644 --- a/funding.json +++ b/funding.json @@ -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", From e3739bce8e2700b407d80dd97fd4c1dcd6a834db Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 5 Mar 2025 14:15:26 +0100 Subject: [PATCH 219/362] test example for rejecting updates --- tests/undo-redo.tests.js | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index df9f9ab38..487601e25 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -116,6 +116,57 @@ 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 { + 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 From 35c030d834a428f0e9acc3a2fd9b9258c0c24ff5 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 6 Mar 2025 10:36:18 +0100 Subject: [PATCH 220/362] improve reject update example --- tests/undo-redo.tests.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 487601e25..faf42affd 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -152,7 +152,9 @@ export const testRejectUpdateExample = _tc => { try { Y.applyUpdate(ydoc, update, 'remote change') } finally { - um.undo() + while (um.undoStack.length) { + um.undo() + } um.destroy() ydoc.store.pendingDs = beforePendingDs ydoc.store.pendingStructs = null From 3ecfb4e898076ff3cd4d83001be2721065c43acf Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 9 Mar 2025 20:57:52 +0100 Subject: [PATCH 221/362] add rowsncolumns --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c7cbc2745..141e62bfc 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,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 From 06cd5b52aa4ee5a573625527bd92b7025e41fdaf Mon Sep 17 00:00:00 2001 From: Florian Sesser Date: Tue, 18 Mar 2025 19:22:02 +0100 Subject: [PATCH 222/362] README: Remove a duplicate SageMaker mention AWS SageMaker was mentioned twice. Remove one of the mentions. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 141e62bfc..e5348a574 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,6 @@ Showcase](https://yjs-diagram.synergy.codes/). 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 * [Multi.app](https://multi.app) - Multiplayer app sharing: Point, draw and edit From 2ef9ccd1701235c6a649b1580f2b6702b230c842 Mon Sep 17 00:00:00 2001 From: Florian Sesser Date: Tue, 18 Mar 2025 19:23:51 +0100 Subject: [PATCH 223/362] README: Remove duplicate btw mention 'btw' was mentioned twice; remove one mention. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 141e62bfc..0a4aed5ce 100644 --- a/README.md +++ b/README.md @@ -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 From d5b5e7a9a19ab36130fcf2c080268afc72f3869b Mon Sep 17 00:00:00 2001 From: kapil verma Date: Wed, 26 Mar 2025 01:55:02 +0530 Subject: [PATCH 224/362] Update README.md to add https://github.com/kapv89/k_yrs_go to persistence providers Ref: https://x.com/kevin_jahns/status/1904252641124753641 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0a4aed5ce..d9711f407 100644 --- a/README.md +++ b/README.md @@ -300,6 +300,10 @@ A database and connection provider for Yjs based on Firestore. Provides persistent storage for a web server using PostgreSQL and is easily compatible with y-websocket. +
k_yrs_go
+
+ Golang database server for YJS CRDT using Postgres + Redis +
### Tooling From 1b006dc34e096893ccde65812de402e4b363c07f Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 31 Mar 2025 17:34:58 +0800 Subject: [PATCH 225/362] Update README.md Add Kedyou as a user of Yjs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d9711f407..2151583b4 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [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 +* [Kedyou](https://kedyou.com/) - Digital workspaces for tutoring ## Table of Contents From 66989ca14038e66fc36eae92a3f28bc402e6d035 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Tue, 1 Apr 2025 08:04:17 +0530 Subject: [PATCH 226/362] feat: add ace bindings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2151583b4..ae3fc76d3 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ are implemented in separate modules. | [Quill](https://quilljs.com/) | ✔ | [y-quill](https://github.com/yjs/y-quill) | [demo](https://demos.yjs.dev/quill/quill.html) | | [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](https://github.com/yjs/y-codemirror) | [demo](https://demos.yjs.dev/codemirror/codemirror.html) | | [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](https://github.com/yjs/y-monaco) | [demo](https://demos.yjs.dev/monaco/monaco.html) | -| [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | +| [Ace](https://ace.c9.io/) | ✔ | [y-ace](https://github.com/bajrangCoder/y-ace) | | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | From f1f96e1d87e8cb3f8882979acdb6fe8493c28591 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 1 Apr 2025 11:02:23 +0200 Subject: [PATCH 227/362] lint --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e3936720..eab49f841 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,8 @@ are implemented in separate modules. | [Quill](https://quilljs.com/) | ✔ | [y-quill](https://github.com/yjs/y-quill) | [demo](https://demos.yjs.dev/quill/quill.html) | | [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](https://github.com/yjs/y-codemirror) | [demo](https://demos.yjs.dev/codemirror/codemirror.html) | | [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](https://github.com/yjs/y-monaco) | [demo](https://demos.yjs.dev/monaco/monaco.html) | -| [Ace](https://ace.c9.io/) | ✔ | [y-ace](https://github.com/bajrangCoder/y-ace) | | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | +| [Ace](https://ace.c9.io/) | ✔ | [y-ace](https://github.com/bajrangCoder/y-ace) | | +| [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | From 2b621ebe568bb7b01ad25aa28731e76cf3ea306b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 1 Apr 2025 20:07:19 +0200 Subject: [PATCH 228/362] add JupyterGIS --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index eab49f841..cca04a2c2 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,8 @@ Showcase](https://yjs-diagram.synergy.codes/). * [JupyterLab](https://jupyter.org/) Collaborative computational Notebooks * [JupyterCad](https://jupytercad.readthedocs.io/en/latest/) Extension to JupyterLab that enables collaborative editing of 3d FreeCAD Models. +* [JupyterGIS](https://github.com/geojupyter/jupytergis) Collaborative GIS + (Geographic Information System) editor in Jupyter * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon From 4d671671b31344db13da9a7961c9e084f6eb81ca Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 3 Apr 2025 00:18:36 +0200 Subject: [PATCH 229/362] add yrs-warp to providers --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cca04a2c2..8be527494 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,7 @@ collaborative app.
A module that contains a simple websocket backend and a websocket client that connects to that backend. y-redis, -y-sweet, ypy-websocket and +y-sweet, ypy-websocket, yrs-warp and Hocuspocus (see below) are alternative backends to y-websocket.
From b9aa098b04f02f7204abaaa2f3b2f6b50d4e1383 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 7 Apr 2025 06:48:54 +0200 Subject: [PATCH 230/362] add y-op-sqlite --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8be527494..430ad32d2 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,11 @@ A database and connection provider for Yjs based on Firestore.
Golang database server for YJS CRDT using Postgres + Redis
+
y-op-sqlite
+
+ Yjs persistence provider for op-sqlite +
+ ### Tooling From c2097e71e7da651e3fb7c89621919944c6dc4d4b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 18 Apr 2025 14:15:13 +0200 Subject: [PATCH 231/362] fix iterating through deleted structs when they dont exist --- src/utils/DeleteSet.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index fe07b7ceb..17695b08d 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -58,8 +58,9 @@ export class DeleteSet { export const iterateDeletedStructs = (transaction, ds, f) => ds.clients.forEach((deletes, clientid) => { const structs = /** @type {Array} */ (transaction.doc.store.clients.get(clientid)) - for (let i = 0; i < deletes.length; i++) { - const del = deletes[i] + const lastStruct = structs[structs.length - 1] + const clockState = lastStruct.id.clock + lastStruct.length + for (let i = 0, del = deletes[i]; i < deletes.length && del.clock < clockState; del = deletes[++i]) { iterateStructs(transaction, structs, del.clock, del.len, f) } }) From e86622219358dbd34e08617d59e1aafed5d640dc Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 18 Apr 2025 14:20:36 +0200 Subject: [PATCH 232/362] lint --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 430ad32d2..cd4e1d978 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Showcase](https://yjs-diagram.synergy.codes/). * [JupyterCad](https://jupytercad.readthedocs.io/en/latest/) Extension to JupyterLab that enables collaborative editing of 3d FreeCAD Models. * [JupyterGIS](https://github.com/geojupyter/jupytergis) Collaborative GIS - (Geographic Information System) editor in Jupyter + (Geographic Information System) editor in Jupyter * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for sharing analyses, documentation, spreadsheets, and dashboards. * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon @@ -309,7 +309,7 @@ A database and connection provider for Yjs based on Firestore.
y-op-sqlite
- Yjs persistence provider for op-sqlite + Yjs persistence provider for op-sqlite
From 0ae8d6cf5a3e5165fa47f8f77ff5ad656139422d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 18 Apr 2025 14:22:09 +0200 Subject: [PATCH 233/362] 13.6.25 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 31af45d88..c01fc0830 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.24", + "version": "13.6.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.24", + "version": "13.6.25", "license": "MIT", "dependencies": { "lib0": "^0.2.99" diff --git a/package.json b/package.json index 18854b84d..4797373ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.24", + "version": "13.6.25", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 13cd563c53e688581eae844ebab39ce9bd315936 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 18 Apr 2025 16:10:35 +0200 Subject: [PATCH 234/362] [iterateStructs] additional check for non-existent structs --- src/utils/DeleteSet.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 17695b08d..94e32ddb6 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -58,10 +58,12 @@ export class DeleteSet { export const iterateDeletedStructs = (transaction, ds, f) => ds.clients.forEach((deletes, clientid) => { const structs = /** @type {Array} */ (transaction.doc.store.clients.get(clientid)) - const lastStruct = structs[structs.length - 1] - const clockState = lastStruct.id.clock + lastStruct.length - for (let i = 0, del = deletes[i]; i < deletes.length && del.clock < clockState; del = deletes[++i]) { - iterateStructs(transaction, structs, del.clock, del.len, f) + if (structs != null) { + const lastStruct = structs[structs.length - 1] + const clockState = lastStruct.id.clock + lastStruct.length + for (let i = 0, del = deletes[i]; i < deletes.length && del.clock < clockState; del = deletes[++i]) { + iterateStructs(transaction, structs, del.clock, del.len, f) + } } }) From 2f4b8c38a1255548f310ed9b1fbc095f39f451fc Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 18 Apr 2025 16:12:26 +0200 Subject: [PATCH 235/362] 13.6.26 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c01fc0830..355060874 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.25", + "version": "13.6.26", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.25", + "version": "13.6.26", "license": "MIT", "dependencies": { "lib0": "^0.2.99" diff --git a/package.json b/package.json index 4797373ef..8e0643eb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.25", + "version": "13.6.26", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2fbc73e8f8acebe6b08c7f61e9ec230e11ea540e Mon Sep 17 00:00:00 2001 From: Yeshan Kaushik <68059178+Yeshan-K@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:39:09 +0530 Subject: [PATCH 236/362] Update README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index cd4e1d978..e9ebd08d5 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,22 @@ are implemented in separate modules. | [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) | | +### Utilities + +
+
y-utility
+
+Library with YMultiDocUndoManager (undo/redo across Yjs docs) and YKeyValue (optimized key-value store). +
+ +
yjs-orderedtree 🌳
+
+Class for ordered trees via Y.Map. Handles insert, delete, and move operations for folder-like hierarchies. +
+ +
+ + ### Providers Setting up the communication between clients, managing awareness information, From 120856dfaf0a8ede92bc4fda30d94bd2825bb4d6 Mon Sep 17 00:00:00 2001 From: Yeshan Kaushik <68059178+Yeshan-K@users.noreply.github.com> Date: Tue, 22 Apr 2025 20:44:16 +0530 Subject: [PATCH 237/362] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e9ebd08d5..fbdac5d07 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,7 @@ are implemented in separate modules. | [Rows n'Columns](https://www.rowsncolumns.app/) | ✔ | [@rowsncolumns/y-spreadsheet](https://docs.rowsncolumns.app/collaboration/yjs-collaboration) | | ### Utilities +Tools that extend the core functionality of Yjs.
y-utility
From 50378316faa9b9823460fc3974b658e925dae471 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 27 Apr 2025 18:58:33 +0200 Subject: [PATCH 238/362] add funding manifest to .well-known --- .well-known/funding-manifest-urls | 1 + 1 file changed, 1 insertion(+) create mode 100644 .well-known/funding-manifest-urls diff --git a/.well-known/funding-manifest-urls b/.well-known/funding-manifest-urls new file mode 100644 index 000000000..bc204ec64 --- /dev/null +++ b/.well-known/funding-manifest-urls @@ -0,0 +1 @@ +https://yjs.dev From 780b27bdba9966118f3b74941452babf0b509802 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 27 Apr 2025 19:27:14 +0200 Subject: [PATCH 239/362] update funding manifest --- funding.json | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/funding.json b/funding.json index 3ae1c2664..5ac8cf88c 100644 --- a/funding.json +++ b/funding.json @@ -8,7 +8,8 @@ "phone": "", "description": "OSS Developer", "webpageUrl": { - "url": "https://github.com/yjs" + "url": "https://yjs.dev", + "wellKnown": "https://yjs.dev/.well-known/funding-manifest-urls" } }, "projects": [ @@ -17,7 +18,8 @@ "name": "Yjs", "description": "A library for building collaborative applications. #p2p #local-first #CRDT Funding this project will also enable me to maintain the other Yjs-related technologies.", "webpageUrl": { - "url": "https://github.com/yjs/yjs" + "url": "https://yjs.dev", + "wellKnown": "https://yjs.dev/.well-known/funding-manifest-urls" }, "repositoryUrl": { "url": "https://github.com/yjs/yjs" @@ -34,8 +36,8 @@ ] }, { - "guid": "Titanic", - "name": "Y/Titanic", + "guid": "titanic", + "name": "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/titanic", @@ -64,14 +66,20 @@ { "guid": "github-sponsors", "type": "payment-provider", - "address": "", + "address": "https://github.com/sponsors/dmonad", "description": "For funding of the Yjs project" }, { - "guid": "y-collective", + "guid": "yjs-opencollective", + "type": "payment-provider", + "address": "https://opencollective.com/y-collective/projects/yjs", + "description": "For funding Yjs via the OpenCollective." + }, + { + "guid": "ycrdt-opencollective", "type": "payment-provider", - "address": "https://opencollective.com/y-collective", - "description": "For funding the Y-CRDT - the Rust implementation of Yjs and other listed projects." + "address": "https://opencollective.com/y-collective/projects/y-crdt", + "description": "For funding Y-CRDT via the OpenCollective." } ], "plans": [ @@ -85,7 +93,7 @@ "frequency": "monthly", "channels": [ "github-sponsors", - "y-collective" + "yjs-opencollective" ] }, { @@ -97,7 +105,8 @@ "currency": "USD", "frequency": "one-time", "channels": [ - "github-sponsors" + "github-sponsors", + "yjs-opencollective" ] }, { @@ -109,7 +118,8 @@ "currency": "USD", "frequency": "monthly", "channels": [ - "github-sponsors" + "github-sponsors", + "yjs-opencollective" ] }, { @@ -121,7 +131,8 @@ "currency": "USD", "frequency": "monthly", "channels": [ - "github-sponsors" + "github-sponsors", + "yjs-opencollective" ] }, { @@ -133,7 +144,8 @@ "currency": "USD", "frequency": "monthly", "channels": [ - "github-sponsors" + "github-sponsors", + "yjs-opencollective" ] } ], From 29ab38cc431e38ef10aea8ad00b9e086806ef861 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 27 Apr 2025 19:42:00 +0200 Subject: [PATCH 240/362] add y-crdt --- funding.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/funding.json b/funding.json index 5ac8cf88c..d326780ea 100644 --- a/funding.json +++ b/funding.json @@ -59,6 +59,32 @@ "real-time", "web-development" ] + }, + { + "guid": "y-crdt", + "name": "Y-CRDT", + "description": "Rust implementation of Yjs with many bindings to other languages (C#, Python, Cffi, Wasm, Swift, ..)", + "webpageUrl": { + "url": "https://github.com/y-crdt/y-crdt", + "wellKnown": "https://github.com/y-crdt/y-crdt/blob/main/.well-known/funding-manifest-urls" + }, + "repositoryUrl": { + "url": "https://github.com/y-crdt/y-crdt", + "wellKnown": "https://github.com/y-crdt/y-crdt/blob/main/.well-known/funding-manifest-urls" + }, + "licenses": [ + "spdx:MIT" + ], + "tags": [ + "privacy", + "collaboration", + "p2p", + "CRDT", + "rich-text", + "real-time", + "web-development" + ] + } ], "funding": { From 120d61176cd5d355336d9d91a6fd87f79385607e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 27 Apr 2025 19:48:11 +0200 Subject: [PATCH 241/362] better description of myself --- funding.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/funding.json b/funding.json index d326780ea..be198c3c8 100644 --- a/funding.json +++ b/funding.json @@ -6,7 +6,7 @@ "name": "Kevin Jahns", "email": "kevin.jahns@protonmail.com", "phone": "", - "description": "OSS Developer", + "description": "Independent OSS Developer maintaining Yjs and many related libraries. My goal is to make the web more (real-time) collaborative.", "webpageUrl": { "url": "https://yjs.dev", "wellKnown": "https://yjs.dev/.well-known/funding-manifest-urls" From 2f33d1665e57385d4cb2beebb81e1da841edd6b1 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 27 Apr 2025 19:52:50 +0200 Subject: [PATCH 242/362] lint --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fbdac5d07..4b1d9b108 100644 --- a/README.md +++ b/README.md @@ -170,22 +170,26 @@ are implemented in separate modules. | [Rows n'Columns](https://www.rowsncolumns.app/) | ✔ | [@rowsncolumns/y-spreadsheet](https://docs.rowsncolumns.app/collaboration/yjs-collaboration) | | ### Utilities + Tools that extend the core functionality of Yjs.
y-utility
-Library with YMultiDocUndoManager (undo/redo across Yjs docs) and YKeyValue (optimized key-value store). +Library with YMultiDocUndoManager (undo/redo across Yjs docs) and +YKeyValue (optimized key-value store).
- -
yjs-orderedtree 🌳
+
+ yjs-orderedtree 🌳 +
-Class for ordered trees via Y.Map. Handles insert, delete, and move operations for folder-like hierarchies. +Class for ordered trees via Y.Map. Handles insert, +delete, and move operations for folder-like +hierarchies.
- ### Providers Setting up the communication between clients, managing awareness information, From bd645c47bffd57ca68e406194eef9f811d0ca84a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Apr 2025 18:26:02 +0200 Subject: [PATCH 243/362] remove y-crdt (separate funding.json is maintained in y-crdt org) --- funding.json | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/funding.json b/funding.json index be198c3c8..03740f205 100644 --- a/funding.json +++ b/funding.json @@ -59,32 +59,6 @@ "real-time", "web-development" ] - }, - { - "guid": "y-crdt", - "name": "Y-CRDT", - "description": "Rust implementation of Yjs with many bindings to other languages (C#, Python, Cffi, Wasm, Swift, ..)", - "webpageUrl": { - "url": "https://github.com/y-crdt/y-crdt", - "wellKnown": "https://github.com/y-crdt/y-crdt/blob/main/.well-known/funding-manifest-urls" - }, - "repositoryUrl": { - "url": "https://github.com/y-crdt/y-crdt", - "wellKnown": "https://github.com/y-crdt/y-crdt/blob/main/.well-known/funding-manifest-urls" - }, - "licenses": [ - "spdx:MIT" - ], - "tags": [ - "privacy", - "collaboration", - "p2p", - "CRDT", - "rich-text", - "real-time", - "web-development" - ] - } ], "funding": { @@ -100,12 +74,6 @@ "type": "payment-provider", "address": "https://opencollective.com/y-collective/projects/yjs", "description": "For funding Yjs via the OpenCollective." - }, - { - "guid": "ycrdt-opencollective", - "type": "payment-provider", - "address": "https://opencollective.com/y-collective/projects/y-crdt", - "description": "For funding Y-CRDT via the OpenCollective." } ], "plans": [ From 7dad24d7b630587081cacc6ca4fe8db17c1e6def Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 13 May 2025 16:25:40 +0200 Subject: [PATCH 244/362] support bigints in Y.Map.set - fixes #711 --- src/types/AbstractType.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 019369ecf..a83f3a58d 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -857,6 +857,8 @@ export const typeMapSet = (transaction, parent, key, value) => { case Boolean: case Array: case String: + case Date: + case BigInt: content = new ContentAny([value]) break case Uint8Array: From 987c9ebb5ad0a2a89a0230f3a0c6b31f095d5f2d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 13 May 2025 16:32:59 +0200 Subject: [PATCH 245/362] 13.6.27 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 355060874..c2a1194ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "13.6.26", + "version": "13.6.27", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "13.6.26", + "version": "13.6.27", "license": "MIT", "dependencies": { "lib0": "^0.2.99" diff --git a/package.json b/package.json index 8e0643eb7..2f22d074e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.26", + "version": "13.6.27", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 4ea9a75cf592a4dd6fb9c0d90e8384e2ec77486f Mon Sep 17 00:00:00 2001 From: i3dly Date: Mon, 26 May 2025 13:07:45 -0700 Subject: [PATCH 246/362] README: add pluv.io provider Added the pluv.io Yjs provider to the README. GitHub: https://github.com/pluv-io/pluv Docs: https://pluv.io/docs/storage/using-yjs#Yjs-Provider --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 4b1d9b108..0fdd300bc 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,14 @@ A standalone extensible yjs server with sqlite persistence, webhooks, auth and m
Cloud service for building multiplayer apps.
+ +
@pluv/crdt-yjs
+
+Use pluv.io as a +full-featured backend for Yjs. pluv.io can either be be used on its +fully-managed WebSocket infrastructure, or self-hosted on Cloudflare Workers +and Node.js runtimes. Offers a typesafe API with authentication, webhooks, +rooms, and more.
y-libp2p
Uses libp2p to propagate updates via From 2d09ed44cf46cd75cc22fa3d4fb1433069baf35c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 27 May 2025 10:06:08 +0200 Subject: [PATCH 247/362] add electric-sql as a provider --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b1d9b108..b0a5b9504 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,10 @@ Provider for sharing data in webxdc chat apps.
An architecture to relay end-to-end encrypted CRDTs over a central service.
- +
y-electric
+
+ Sync Yjs over ElectricSQL. +
#### Persistence Providers From 273c53b7ef69039b07b285ecc63ded498f02ad17 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 27 May 2025 16:47:40 +0200 Subject: [PATCH 248/362] add yjs-cf-ws-provider --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b0a5b9504..dfcc685da 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,9 @@ An architecture to relay end-to-end encrypted CRDTs over a central service.
Sync Yjs over ElectricSQL.
+
yjs-cf-ws-provider
+ Cloudflare provider for Yjs based on durable objects. +
#### Persistence Providers From 55df734c1433dcfb87e5ee688784529bf6ee8677 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 10 Jun 2025 16:00:35 +0200 Subject: [PATCH 249/362] add lightpage as a user --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dfcc685da..4ad25e62b 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ Showcase](https://yjs-diagram.synergy.codes/). editing for your IDE or custom editor * [Typst](https://typst.app/) - Compose, edit, and automate technical documents * [Kedyou](https://kedyou.com/) - Digital workspaces for tutoring +* [Lightpage](https://lightpage.com/) - Personal living notebook ## Table of Contents From 7310314fcbc05670e8c70bb89038f7fbeed0d402 Mon Sep 17 00:00:00 2001 From: xy Date: Sun, 8 Jun 2025 03:39:13 +0900 Subject: [PATCH 250/362] Add reearth-flow to the README as a collaborative data calculation tool --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4ad25e62b..bc634341f 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,8 @@ editing for your IDE or custom editor * [Typst](https://typst.app/) - Compose, edit, and automate technical documents * [Kedyou](https://kedyou.com/) - Digital workspaces for tutoring * [Lightpage](https://lightpage.com/) - Personal living notebook +* [reearth-flow](https://github.com/reearth/reearth-flow) - + Collaboratively calculate and convert various data ## Table of Contents From bc941a57a7ef0fe0912b752c063d457f1913bcc0 Mon Sep 17 00:00:00 2001 From: Clo Junseo Kim Date: Sun, 15 Jun 2025 23:59:21 +0900 Subject: [PATCH 251/362] README: Update y-sweet debugger hyperlink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ad25e62b..a0e843c1f 100644 --- a/README.md +++ b/README.md @@ -344,7 +344,7 @@ A database and connection provider for Yjs based on Firestore. ### Tooling -* [y-sweet debugger](https://docs.jamsocket.com/y-sweet/advanced/debugger) +* [y-sweet debugger](https://y-sweet.cloud/advanced/debugger) * [liveblocks devtools](https://liveblocks.io/devtools) * [Yjs inspector](https://inspector.yjs.dev) From fcfdba15019edb23996dd7f50e43192242af5f4a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 19 Jun 2025 14:08:42 +0200 Subject: [PATCH 252/362] added superdoc as one of the providers --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a0e843c1f..19118546e 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ are implemented in separate modules. | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | +| [Superdoc](https://superdoc.dev/) | ✔ | (native) | [demo](https://superdoc.dev/) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | | React | | [react-yjs](https://github.com/nikgraf/react-yjs) | [demo](https://react-yjs-example.vercel.app/) | From 61258d84528420636cd9f1c51fc9915bbd24311d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 19 Jun 2025 14:17:46 +0200 Subject: [PATCH 253/362] add blocknote --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19118546e..0b4abee40 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ are implemented in separate modules. | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) | | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | +| [BlockNote](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | | [Superdoc](https://superdoc.dev/) | ✔ | (native) | [demo](https://superdoc.dev/) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | @@ -298,9 +299,14 @@ An architecture to relay end-to-end encrypted CRDTs over a central service.
Sync Yjs over ElectricSQL.
-
yjs-cf-ws-provider
+
yjs-cf-ws-provider
+
Cloudflare provider for Yjs based on durable objects.
+
nostr-crdt
+
+ Sync Yjs over nostr. +
#### Persistence Providers From c87430ae5a4db23c9f6e4f24742d7247232ea641 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 19 Jun 2025 14:24:28 +0200 Subject: [PATCH 254/362] add milkdown --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0b4abee40..045299a17 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ are implemented in separate modules. | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | | [BlockNote](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | +| [Milkdown](https://github.com/Milkdown/milkdown) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://milkdown.dev/playground) | | [Superdoc](https://superdoc.dev/) | ✔ | (native) | [demo](https://superdoc.dev/) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) | From 0b8a0fca8d4e48d0b9a2b7b16457489027eeb10f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 19 Jun 2025 14:29:31 +0200 Subject: [PATCH 255/362] add tiptap --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 045299a17..0990c43a0 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ are implemented in separate modules. | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) | | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) | | [BlockNote](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | +| [Tiptap](https://tiptap.dev/) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://template.tiptap.dev/preview/templates/simple) | | [Milkdown](https://github.com/Milkdown/milkdown) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://milkdown.dev/playground) | | [Superdoc](https://superdoc.dev/) | ✔ | (native) | [demo](https://superdoc.dev/) | | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) | From f4fa053272014873b56a827f17c16dca025cc308 Mon Sep 17 00:00:00 2001 From: hyonun321 Date: Wed, 25 Jun 2025 21:06:12 +0900 Subject: [PATCH 256/362] fix typos - Fix 'randon' to 'random' in tests/testHelper.js - Fix 'successfull' to 'successful' in funding.json --- funding.json | 2 +- tests/testHelper.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/funding.json b/funding.json index 03740f205..676c306e1 100644 --- a/funding.json +++ b/funding.json @@ -120,7 +120,7 @@ "guid": "silver-sponsor", "status": "active", "name": "Silver Sponsor", - "description": "This is the recommended plan for large/successfull companies that use Yjs.", + "description": "This is the recommended plan for large/successful companies that use Yjs.", "amount": 1000, "currency": "USD", "frequency": "monthly", diff --git a/tests/testHelper.js b/tests/testHelper.js index 569836794..0fa651d1a 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -227,7 +227,7 @@ export class TestConnector { } /** - * @return {boolean} Whether it was possible to disconnect a randon connection. + * @return {boolean} Whether it was possible to disconnect a random connection. */ disconnectRandom () { if (this.onlineConns.size === 0) { From 783a7c4c1bc5f123638ca2169f51452a5c80d7ba Mon Sep 17 00:00:00 2001 From: Matt Krick Date: Wed, 9 Jul 2025 17:02:57 -0700 Subject: [PATCH 257/362] remove newValue from YEvent --- src/utils/YEvent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index d6a602429..61bc28408 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -39,7 +39,7 @@ export class YEvent { */ this._changes = null /** - * @type {null | Map} + * @type {null | Map} */ this._keys = null /** @@ -82,7 +82,7 @@ export class YEvent { } /** - * @type {Map} + * @type {Map} */ get keys () { if (this._keys === null) { From 4738944446333d71821479990a41d9bf2bab6213 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 25 Mar 2025 11:14:55 +0100 Subject: [PATCH 258/362] Run tests without compilation. optimize testHelper output. --- package-lock.json | 766 ++++++++++++++++----------------------- package.json | 44 ++- rollup.config.js | 108 ++---- test.html | 322 +++++++++++++++- tests/index.js | 8 +- tests/undo-redo.tests.js | 3 +- tests/updates.tests.js | 2 +- tests/y-array.tests.js | 1 - tests/y-map.tests.js | 4 +- tests/y-xml.tests.js | 3 +- tsconfig.json | 4 +- 11 files changed, 696 insertions(+), 569 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2a1194ad..1d07c1782 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,21 +9,19 @@ "version": "13.6.27", "license": "MIT", "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": { "node": ">=16.0.0", @@ -233,12 +231,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -249,83 +241,292 @@ "node": ">=14" } }, - "node_modules/@rollup/plugin-commonjs": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", - "integrity": "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", + "integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "glob": "^8.0.3", - "is-reference": "1.2.1", - "magic-string": "^0.27.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.68.0||^3.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz", + "integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "@types/resolve": "1.20.2", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", - "is-module": "^1.0.0", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^2.78.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz", + "integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz", + "integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz", + "integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz", + "integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz", + "integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz", + "integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz", + "integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz", + "integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz", + "integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz", + "integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz", + "integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz", + "integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz", + "integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz", + "integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz", + "integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz", + "integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz", + "integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz", + "integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json5": { "version": "0.0.29", @@ -364,12 +565,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true - }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -545,15 +740,6 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", @@ -572,15 +758,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/basic-auth": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha512-CtGuTyWf3ig+sgRyC7uP6DM3N+5ur/p8L+FPfsd+BbIfIs74TFfCajZTHnCw6K5dqM0bZEbRIqRy1fAdiUJhTA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -602,18 +779,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/call-bind": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", @@ -736,15 +901,6 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, "node_modules/commander": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", @@ -754,12 +910,6 @@ "node": ">= 0.6.x" } }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -790,15 +940,6 @@ "node": ">=4.0.0" } }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -864,15 +1005,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/define-data-property": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", @@ -964,22 +1096,6 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "node_modules/ecstatic": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz", - "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==", - "deprecated": "This package is unmaintained and deprecated. See the GH Issue 259.", - "dev": true, - "dependencies": { - "he": "^1.1.1", - "mime": "^1.6.0", - "minimist": "^1.1.0", - "url-join": "^2.0.5" - }, - "bin": { - "ecstatic": "lib/ecstatic.js" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1761,12 +1877,6 @@ "node": ">=4.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -1776,12 +1886,6 @@ "node": ">=0.10.0" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1844,26 +1948,6 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -1901,6 +1985,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1999,25 +2084,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -2168,15 +2234,6 @@ "node": ">= 0.4" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -2203,45 +2260,6 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-server": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.12.3.tgz", - "integrity": "sha512-be0dKG6pni92bRjq0kvExtj/NrrAd28/8fCXkaI/4piTwQMSDSLMhWyW0NI1V+DBI3aa1HMlQu46/HjVLfmugA==", - "dev": true, - "dependencies": { - "basic-auth": "^1.0.3", - "colors": "^1.4.0", - "corser": "^2.0.1", - "ecstatic": "^3.3.2", - "http-proxy": "^1.18.0", - "minimist": "^1.2.5", - "opener": "^1.5.1", - "portfinder": "^1.0.25", - "secure-compare": "3.0.1", - "union": "~0.5.0" - }, - "bin": { - "hs": "bin/http-server", - "http-server": "bin/http-server" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2365,21 +2383,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "dependencies": { - "builtin-modules": "^3.3.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -2449,12 +2452,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -2482,15 +2479,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -2785,9 +2773,10 @@ } }, "node_modules/lib0": { - "version": "0.2.99", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.99.tgz", - "integrity": "sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==", + "version": "0.2.101", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.101.tgz", + "integrity": "sha512-LljA6+Ehf0Z7YnxhgSAvspzWALjW4wlWdN/W4iGiqYc1KvXQgOVXWI0xwlwqozIL5WRdKeUW2gq0DLhFsY+Xlw==", + "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -2949,18 +2938,6 @@ "node": ">=10" } }, - "node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", @@ -3164,30 +3141,6 @@ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -3366,15 +3319,6 @@ "wrappy": "1" } }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true, - "bin": { - "opener": "bin/opener-bin.js" - } - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -3523,18 +3467,6 @@ "node": ">=4" } }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -3664,32 +3596,6 @@ "node": ">=4" } }, - "node_modules/portfinder": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", - "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", - "dev": true, - "dependencies": { - "async": "^2.6.4", - "debug": "^3.2.7", - "mkdirp": "^0.5.6" - }, - "engines": { - "node": ">= 0.12.0" - } - }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3737,21 +3643,6 @@ "node": ">=6" } }, - "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3837,12 +3728,6 @@ "node": ">=0.10.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, "node_modules/requizzle": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", @@ -3936,18 +3821,42 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "4.37.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz", + "integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==", "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.37.0", + "@rollup/rollup-android-arm64": "4.37.0", + "@rollup/rollup-darwin-arm64": "4.37.0", + "@rollup/rollup-darwin-x64": "4.37.0", + "@rollup/rollup-freebsd-arm64": "4.37.0", + "@rollup/rollup-freebsd-x64": "4.37.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", + "@rollup/rollup-linux-arm-musleabihf": "4.37.0", + "@rollup/rollup-linux-arm64-gnu": "4.37.0", + "@rollup/rollup-linux-arm64-musl": "4.37.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", + "@rollup/rollup-linux-riscv64-gnu": "4.37.0", + "@rollup/rollup-linux-riscv64-musl": "4.37.0", + "@rollup/rollup-linux-s390x-gnu": "4.37.0", + "@rollup/rollup-linux-x64-gnu": "4.37.0", + "@rollup/rollup-linux-x64-musl": "4.37.0", + "@rollup/rollup-win32-arm64-msvc": "4.37.0", + "@rollup/rollup-win32-ia32-msvc": "4.37.0", + "@rollup/rollup-win32-x64-msvc": "4.37.0", "fsevents": "~2.3.2" } }, @@ -4027,12 +3936,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true - }, "node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -4689,18 +4592,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "dependencies": { - "qs": "^6.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4710,12 +4601,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", - "integrity": "sha512-c2H1fIgpUdwFRIru9HFno5DT73Ok8hg5oOb5AT3ayIgvCRfxgs2jyt5Slw8kEB7j3QUr6yJmMPDT/odjk7jXow==", - "dev": true - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4943,7 +4828,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", - "dev": true, "dependencies": { "lib0": "^0.2.85" }, @@ -4966,22 +4850,8 @@ "dev": true }, "node_modules/yjs": { - "version": "13.6.11", - "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.11.tgz", - "integrity": "sha512-FvRRJKX9u270dOLkllGF/UDCWwmIv2Z+ucM4v1QO1TuxdmoiMnSUXH1HAcOKOrkBEhQtPTkxep7tD2DrQB+l0g==", - "dev": true, - "peer": true, - "dependencies": { - "lib0": "^0.2.86" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } + "resolved": "", + "link": true } } } diff --git a/package.json b/package.json index 2f22d074e..448c39be8 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/rollup.config.js b/rollup.config.js index 61afb735e..144eb6432 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -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} - */ -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', + entryFileNames : '[name].cjs', sourcemap: true }, - external: id => /^lib0\//.test(id) + plugins: [ + resolver + ], + external: id => /^(lib0|y-protocols)\//.test(id) }, { - input: './src/index.js', - output: { - name: 'Y', - file: 'dist/yjs.mjs', - format: 'esm', - sourcemap: true + // esm output + input: { + yjs: './src/index.js', + testHelper: './tests/testHelper.js', + internals: './src/internals.js' }, - external: id => /^lib0\//.test(id) -}, { - input: './tests/testHelper.js', output: { - name: 'Y', - file: 'dist/testHelper.mjs', + dir: 'dist', 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', - sourcemap: true - }, - plugins: [ - debugResolve, - nodeResolve({ - mainFields: ['browser', 'module', 'main'] - }), - commonjs() - ] -}, { - input: './tests/index.js', - output: { - name: 'test', - file: 'dist/tests.cjs', - format: 'cjs', + entryFileNames : '[name].mjs', 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) }] diff --git a/test.html b/test.html index c3b3ab2de..28bf9ad35 100644 --- a/test.html +++ b/test.html @@ -1,9 +1,327 @@ + - Testing Yjs + Testing yjs + - + + diff --git a/tests/index.js b/tests/index.js index fd9c08e79..2799d745f 100644 --- a/tests/index.js +++ b/tests/index.js @@ -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) { diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index faf42affd..46aa855ae 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -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 = () => { diff --git a/tests/updates.tests.js b/tests/updates.tests.js index f3169cfee..12ac149e6 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -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' diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 1f593485b..cae7650c9 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -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' diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 080b71406..23c3f3daa 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -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' diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index a9395c2d6..e2743daef 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -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 = () => { diff --git a/tsconfig.json b/tsconfig.json index 2520c4048..a4f93c9c3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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"] From cfe67c1f8c7cc005ef7733e5e0f5ce1e4395725c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 25 Mar 2025 11:15:17 +0100 Subject: [PATCH 259/362] basic delta implementation --- src/utils/Delta.js | 177 +++++++++++++++++++++++++++++++++++++++++++ tests/delta.tests.js | 11 +++ 2 files changed, 188 insertions(+) create mode 100644 src/utils/Delta.js create mode 100644 tests/delta.tests.js diff --git a/src/utils/Delta.js b/src/utils/Delta.js new file mode 100644 index 000000000..81306e51a --- /dev/null +++ b/src/utils/Delta.js @@ -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} + */ + 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() diff --git a/tests/delta.tests.js b/tests/delta.tests.js new file mode 100644 index 000000000..e3eaf8dd2 --- /dev/null +++ b/tests/delta.tests.js @@ -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' } }]) +} + From e13fc602a82de1191f6da7aa8bff6e97781928ff Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 27 Mar 2025 16:24:11 +0100 Subject: [PATCH 260/362] implement support for diffing deletesets --- src/internals.js | 1 + src/types/YText.js | 95 ++++++++++++++++++- src/utils/AttributionManager.js | 8 ++ src/utils/DeleteSet.js | 77 +++++++++++++++- src/utils/Delta.js | 11 +-- tests/deleteset.tests.js | 157 ++++++++++++++++++++++++++++++++ tests/delta.tests.js | 2 +- tests/index.js | 8 +- 8 files changed, 343 insertions(+), 16 deletions(-) create mode 100644 src/utils/AttributionManager.js create mode 100644 tests/deleteset.tests.js diff --git a/src/internals.js b/src/internals.js index cb2fcac8c..b9fe33bf9 100644 --- a/src/internals.js +++ b/src/internals.js @@ -40,3 +40,4 @@ export * from './structs/ContentString.js' export * from './structs/ContentType.js' export * from './structs/Item.js' export * from './structs/Skip.js' +export * from './utils/AttributionManager.js' diff --git a/src/types/YText.js b/src/types/YText.js index 75bf5dd64..56aec77f7 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -27,9 +27,12 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line + ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, AttributionManager, // eslint-disable-line + snapshot } from '../internals.js' +import * as delta from '../utils/Delta.js' + import * as object from 'lib0/object' import * as map from 'lib0/map' import * as error from 'lib0/error' @@ -996,6 +999,96 @@ export class YText extends AbstractType { } } + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {AttributionManager} [attributionManager] + * @param {Doc} [prevYdoc] + * @return {import('../utils/Delta.js').Delta} The Delta representation of this type. + * + * @public + */ + getContent (attributionManager, prevYdoc) { + this.doc ?? warnPrematureAccess() + const prevSnapshot = prevYdoc ? snapshot(prevYdoc) : null + const d = delta.create() + /** + * @type {{ [key: string]: any }} + */ + const currentAttributes = {} + const doc = /** @type {Doc} */ (this.doc) + const computeContent = () => { + let n = this._start + while (n !== null) { + switch (n.content.constructor) { + case ContentString: { + const cur = currentAttributes.get('ychange') + if (snapshot !== undefined && !isVisible(n, snapshot)) { + if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') { + packStr() + currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' }) + } + } else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) { + if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') { + packStr() + currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' }) + } + } else if (cur !== undefined) { + packStr() + currentAttributes.delete('ychange') + } + str += /** @type {ContentString} */ (n.content).str + break + } + case ContentType: + case ContentEmbed: { + packStr() + /** + * @type {Object} + */ + const op = { + insert: n.content.getContent()[0] + } + if (currentAttributes.size > 0) { + const attrs = /** @type {Object} */ ({}) + op.attributes = attrs + currentAttributes.forEach((value, key) => { + attrs[key] = value + }) + } + ops.push(op) + break + } + case ContentFormat: + if (isVisible(n, snapshot)) { + packStr() + updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content)) + } + break + } + n = n.right + } + } + if (prevSnapshot) { + // snapshots are merged again after the transaction, so we need to keep the + // transaction alive until we are done + transact(doc, transaction => { + if (prevSnapshot) { + splitSnapshotAffectedStructs(transaction, prevSnapshot) + } + computeContent() + }, 'cleanup') + } else { + computeContent() + } + return d.done() + } + + /** * Returns the Delta representation of this YText type. * diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js new file mode 100644 index 000000000..205826c4e --- /dev/null +++ b/src/utils/AttributionManager.js @@ -0,0 +1,8 @@ + +export class AttributionManager { + /** + * + */ + constructor () { + } +} diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 94e32ddb6..8d42ba616 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -20,10 +20,12 @@ export class DeleteItem { */ constructor (clock, len) { /** + * @readonly * @type {number} */ this.clock = clock /** + * @readonly * @type {number} */ this.len = len @@ -114,7 +116,7 @@ export const isDeleted = (ds, id) => { * @function */ export const sortAndMergeDeleteSet = ds => { - ds.clients.forEach(dels => { + ds.clients.forEach((dels, client) => { dels.sort((a, b) => a.clock - b.clock) // merge items without filtering or splicing the array // i is the current pointer @@ -125,7 +127,12 @@ export const sortAndMergeDeleteSet = ds => { const left = dels[j - 1] const right = dels[i] if (left.clock + left.len >= right.clock) { - left.len = math.max(left.len, right.clock + right.len - left.clock) + const r = right.clock + right.len - left.clock + if (left.len < r) { + dels[j - 1] = new DeleteItem(left.clock, r) + } + } else if (left.len === 0) { + dels[j - 1] = right } else { if (j < i) { dels[j] = right @@ -133,7 +140,14 @@ export const sortAndMergeDeleteSet = ds => { j++ } } - dels.length = j + if (dels[j - 1].len === 0) { + dels.length = j - 1 + } else { + dels.length = j + } + if (dels.length === 0) { + ds.clients.delete(client) + } }) } @@ -163,6 +177,63 @@ export const mergeDeleteSets = dss => { return merged } +/** + * Remove all ranges from `exclude` from `ds`. The result will contain all ranges from `ds` that are not + * in `exclude`. + * + * @param {DeleteSet} ds + * @param {DeleteSet} exclude + * @return {DeleteSet} + */ +export const diffDeleteSet = (ds, exclude) => { + const res = new DeleteSet() + ds.clients.forEach((ranges, client) => { + /** + * @type {Array} + */ + const resRanges = [] + const excludedRanges = exclude.clients.get(client) ?? [] + let i = 0, j = 0 + let currRange = ranges[0] + while (i < ranges.length && j < excludedRanges.length) { + const e = excludedRanges[j] + if (currRange.clock + currRange.len <= e.clock) { // no overlapping, use next range item + if (currRange.len > 0) resRanges.push(currRange) + currRange = ranges[++i] + } else if (e.clock + e.len <= currRange.clock) { // no overlapping, use next excluded item + j++ + } else if (e.clock <= currRange.clock) { // exclude laps into range (we already know that the ranges somehow collide) + const newClock = e.clock + e.len + const newLen = currRange.clock + currRange.len - newClock + if (newLen > 0) { + currRange = new DeleteItem(newClock, newLen) + j++ + } else { + // this item is completely overwritten. len=0. We can jump to the next range + currRange = ranges[++i] + } + } else { // currRange.clock < e.clock -- range laps into exclude => adjust len + // beginning can't be empty, add it to the result + const nextLen = e.clock - currRange.clock + resRanges.push(new DeleteItem(currRange.clock, nextLen)) + // retain the remaining length after exclude in currRange + currRange = new DeleteItem(currRange.clock + e.len + nextLen, math.max(currRange.len - e.len - nextLen, 0)) + if (currRange.len === 0) currRange = ranges[++i] + j++ + } + } + if (currRange != null) { + resRanges.push(currRange) + } + i++ + while (i < ranges.length) { + resRanges.push(ranges[i++]) + } + if (resRanges.length > 0) res.clients.set(client, resRanges) + }) + return res +} + /** * @param {DeleteSet} ds * @param {number} client diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 81306e51a..e306ab81b 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -1,5 +1,4 @@ import * as object from 'lib0/object' -import * as array from 'lib0/array' /** * @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp @@ -29,11 +28,11 @@ export class InsertOp { this.attribution = attribution } toJSON () { - return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {}) + return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) } } -class DeleteOp { +export class DeleteOp { /** * @param {number} len */ @@ -45,7 +44,7 @@ class DeleteOp { } } -class RetainOp { +export class RetainOp { /** * @param {number} retain * @param {FormattingAttributes|null} attributes @@ -133,7 +132,7 @@ export class DeltaBuilder extends Delta { 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))) + this.ops.push(this._lastOp = new InsertOp(insert, mergeAttrs(this._useAttributes, attributes), mergeAttrs(this._useAttribution, attribution))) } return this } @@ -148,7 +147,7 @@ export class DeltaBuilder extends Delta { 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))) + this.ops.push(this._lastOp = new RetainOp(retain, mergeAttrs(this._useAttributes, attributes), mergeAttrs(this._useAttribution, attribution))) } return this } diff --git a/tests/deleteset.tests.js b/tests/deleteset.tests.js new file mode 100644 index 000000000..eee764ae8 --- /dev/null +++ b/tests/deleteset.tests.js @@ -0,0 +1,157 @@ +import * as t from 'lib0/testing' +import * as d from '../src/utils/DeleteSet.js' + +/** + * @param {Array<[number, number, number]>} ops + */ +const simpleConstructDs = ops => { + const ds = new d.DeleteSet() + ops.forEach(op => { + d.addToDeleteSet(ds, op[0], op[1], op[2]) + }) + d.sortAndMergeDeleteSet(ds) + return ds +} + +/** + * @param {d.DeleteSet} ds1 + * @param {d.DeleteSet} ds2 + */ +const compareDs = (ds1, ds2) => { + t.assert(ds1.clients.size === ds2.clients.size) + ds1.clients.forEach((ranges1, clientid) => { + const ranges2 = ds2.clients.get(clientid) ?? [] + t.assert(ranges1.length === ranges2?.length) + for (let i = 0; i < ranges1.length; i++) { + const d1 = ranges1[i] + const d2 = ranges2[i] + t.assert(d1.len === d2.len && d1.clock == d2.clock) + } + }) +} + +/** + * @param {t.TestCase} _tc + */ +export const testDeletesetMerge = _tc => { + t.group('filter out empty items (1))', () => { + compareDs( + simpleConstructDs([[0, 1, 0]]), + simpleConstructDs([]) + ) + }) + t.group('filter out empty items (2))', () => { + compareDs( + simpleConstructDs([[0, 1, 0], [0, 2, 0]]), + simpleConstructDs([]) + ) + }) + t.group('filter out empty items (3 - end))', () => { + compareDs( + simpleConstructDs([[0, 1, 1], [0, 2, 0]]), + simpleConstructDs([[0, 1, 1]]) + ) + }) + t.group('filter out empty items (4 - middle))', () => { + compareDs( + simpleConstructDs([[0, 1, 1], [0, 2, 0], [0, 3, 1]]), + simpleConstructDs([[0, 1, 1], [0, 3, 1]]) + ) + }) + t.group('filter out empty items (5 - beginning))', () => { + compareDs( + simpleConstructDs([[0, 1, 0], [0, 2, 1], [0, 3, 1]]), + simpleConstructDs([[0, 2, 1], [0, 3, 1]]) + ) + }) + t.group('merge of overlapping deletes', () => { + compareDs( + simpleConstructDs([[0, 1, 2], [0, 0, 2]]), + simpleConstructDs([[0, 0, 3]]) + ) + }) + t.group('construct without hole', () => { + compareDs( + simpleConstructDs([[0, 1, 2], [0, 3, 1]]), + simpleConstructDs([[0, 1, 3]]) + ) + }) +} + +/** + * @param {t.TestCase} _tc + */ +export const testDeletesetDiffing = _tc => { + t.group('simple case (1))', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 1], [0, 3, 1]]), + simpleConstructDs([[0, 3, 1]]) + ), + simpleConstructDs([[0, 1, 1]]) + ) + }) + t.group('subset left', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3]]), + simpleConstructDs([[0, 1, 1]]) + ), + simpleConstructDs([[0, 2, 2]]) + ) + }) + t.group('subset right', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3]]), + simpleConstructDs([[0, 3, 1]]) + ), + simpleConstructDs([[0, 1, 2]]) + ) + }) + t.group('subset middle', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3]]), + simpleConstructDs([[0, 2, 1]]) + ), + simpleConstructDs([[0, 1, 1], [0, 3, 1]]) + ) + }) + t.group('overlapping left', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3]]), + simpleConstructDs([[0, 0, 2]]) + ), + simpleConstructDs([[0, 2, 2]]) + ) + }) + t.group('overlapping right', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3]]), + simpleConstructDs([[0, 3, 5]]) + ), + simpleConstructDs([[0, 1, 2]]) + ) + }) + t.group('overlapping completely', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3]]), + simpleConstructDs([[0, 0, 5]]) + ), + simpleConstructDs([]) + ) + }) + t.group('overlapping into new range', () => { + compareDs( + d.diffDeleteSet( + simpleConstructDs([[0, 1, 3], [0, 5, 2]]), + simpleConstructDs([[0, 0, 6]]) + ), + simpleConstructDs([[0, 6, 1]]) + ) + }) +} diff --git a/tests/delta.tests.js b/tests/delta.tests.js index e3eaf8dd2..01cd08d28 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -6,6 +6,6 @@ import * as delta from '../src/utils/Delta.js' */ 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' } }]) + t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { creator: 'tester' } }]) } diff --git a/tests/index.js b/tests/index.js index 2799d745f..6e7dc6de0 100644 --- a/tests/index.js +++ b/tests/index.js @@ -11,7 +11,8 @@ 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 * as delta from './delta.tests.js' +import * as deleteset from './deleteset.tests.js' import { runTests } from 'lib0/testing' import { isBrowser, isNode } from 'lib0/environment' @@ -21,11 +22,8 @@ if (isBrowser) { log.createVConsole(document.body) } -/** - * @type {any} - */ const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, deleteset } const run = async () => { From 0d3a1497381409be6522c5f3c12e9793bebc8a5a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 28 Mar 2025 19:12:47 +0100 Subject: [PATCH 261/362] add randomized tests for ds --- src/utils/DeleteSet.js | 2 +- tests/deleteset.tests.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index 8d42ba616..3b48bb42f 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -219,7 +219,7 @@ export const diffDeleteSet = (ds, exclude) => { // retain the remaining length after exclude in currRange currRange = new DeleteItem(currRange.clock + e.len + nextLen, math.max(currRange.len - e.len - nextLen, 0)) if (currRange.len === 0) currRange = ranges[++i] - j++ + else j++ } } if (currRange != null) { diff --git a/tests/deleteset.tests.js b/tests/deleteset.tests.js index eee764ae8..c28e26847 100644 --- a/tests/deleteset.tests.js +++ b/tests/deleteset.tests.js @@ -1,5 +1,7 @@ import * as t from 'lib0/testing' import * as d from '../src/utils/DeleteSet.js' +import * as prng from 'lib0/prng' +import * as math from 'lib0/math' /** * @param {Array<[number, number, number]>} ops @@ -155,3 +157,39 @@ export const testDeletesetDiffing = _tc => { ) }) } + +/** + * @param {prng.PRNG} gen + * @param {number} clients + * @param {number} clockRange (max clock - exclusive - by each client) + */ +const createRandomDiffSet = (gen, clients, clockRange) => { + const maxOpLen = 5 + const numOfOps = math.ceil((clients * clockRange) / maxOpLen) + const ds = new d.DeleteSet() + for (let i = 0; i < numOfOps; i++) { + const client = prng.uint32(gen, 0, clients - 1) + const clockStart = prng.uint32(gen, 0, clockRange) + const len = prng.uint32(gen, 0, clockRange - clockStart) + d.addToDeleteSet(ds, client, clockStart, len) + } + d.sortAndMergeDeleteSet(ds) + if (ds.clients.size === clients && clients > 1 && prng.bool(gen)) { + ds.clients.delete(prng.uint32(gen, 0, clients)) + } + return ds +} + +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDiffing = tc => { + const clients = 4 + const clockRange = 100 + const ds1 = createRandomDiffSet(tc.prng, clients, clockRange) + const ds2 = createRandomDiffSet(tc.prng, clients, clockRange) + const merged = d.mergeDeleteSets([ds1, ds2]) + const e1 = d.diffDeleteSet(ds1, ds2) + const e2 = d.diffDeleteSet(merged, ds2) + compareDs(e1, e2) +} From da3cb22147a329582c53da4f47a45366ed8c7734 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 8 Apr 2025 14:37:08 +0200 Subject: [PATCH 262/362] use "insertSet" instead of computing state vectors in transactions --- src/structs/ContentType.js | 5 +- src/structs/GC.js | 4 +- src/structs/Item.js | 4 +- src/types/YText.js | 18 +++---- src/utils/Transaction.js | 98 +++++++++++++++++++++++++++++++------- src/utils/UndoManager.js | 9 +--- src/utils/YEvent.js | 2 +- src/utils/encoding.js | 25 +++++++++- 8 files changed, 120 insertions(+), 45 deletions(-) diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index 630efeb36..9785d6749 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -6,6 +6,7 @@ import { readYXmlFragment, readYXmlHook, readYXmlText, + isDeleted, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, YEvent, AbstractType // eslint-disable-line } from '../internals.js' @@ -107,7 +108,7 @@ export class ContentType { while (item !== null) { if (!item.deleted) { item.delete(transaction) - } else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) { + } else if (!isDeleted(transaction.insertSet, item.id)) { // This will be gc'd later and we want to merge it if possible // We try to merge all deleted items after each transaction, // but we have no knowledge about that this needs to be merged @@ -119,7 +120,7 @@ export class ContentType { this.type._map.forEach(item => { if (!item.deleted) { item.delete(transaction) - } else if (item.id.clock < (transaction.beforeState.get(item.id.client) || 0)) { + } else if (!isDeleted(transaction.insertSet, item.id)) { // same as above transaction._mergeStructs.push(item) } diff --git a/src/structs/GC.js b/src/structs/GC.js index 3c7cec0c3..cdda47151 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -1,7 +1,8 @@ import { AbstractStruct, addStruct, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID, // eslint-disable-line + addItemToInsertSet } from '../internals.js' export const structGCRefNumber = 0 @@ -37,6 +38,7 @@ export class GC extends AbstractStruct { this.id.clock += offset this.length -= offset } + addItemToInsertSet(transaction, this) addStruct(transaction.doc.store, this) } diff --git a/src/structs/Item.js b/src/structs/Item.js index 2d2b1bb92..923db3aac 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -22,7 +22,8 @@ import { readContentType, addChangedTypeToTransaction, isDeleted, - StackItem, DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction // eslint-disable-line + StackItem, DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line + addItemToInsertSet } from '../internals.js' import * as error from 'lib0/error' @@ -514,6 +515,7 @@ export class Item extends AbstractStruct { if (this.parentSub === null && this.countable && !this.deleted) { /** @type {AbstractType} */ (this.parent)._length += this.length } + addItemToInsertSet(transaction, this) addStruct(transaction.doc.store, this) this.content.integrate(transaction, this) // add parent to transaction.changed diff --git a/src/types/YText.js b/src/types/YText.js index 56aec77f7..f902cc85b 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -493,19 +493,13 @@ export const cleanupYTextAfterTransaction = transaction => { const needFullCleanup = new Set() // check if another formatting item was inserted const doc = transaction.doc - for (const [client, afterClock] of transaction.afterState.entries()) { - const clock = transaction.beforeState.get(client) || 0 - if (afterClock === clock) { - continue + iterateDeletedStructs(transaction, transaction.insertSet, (item) => { + if ( + !item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC + ) { + needFullCleanup.add(/** @type {any} */ (item).parent) } - iterateStructs(transaction, /** @type {Array} */ (doc.store.clients.get(client)), clock, afterClock, item => { - if ( - !item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC - ) { - needFullCleanup.add(/** @type {any} */ (item).parent) - } - }) - } + }) // cleanup in a new transaction transact(doc, (t) => { iterateDeletedStructs(transaction, transaction.deleteSet, item => { diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 9792d32ed..e821683a7 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -11,9 +11,12 @@ import { generateNewClientId, createID, cleanupYTextAfterTransaction, - UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line + isDeleted, + UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, // eslint-disable-line + DeleteItem } from '../internals.js' +import * as error from 'lib0/error' import * as map from 'lib0/map' import * as math from 'lib0/math' import * as set from 'lib0/set' @@ -62,16 +65,21 @@ export class Transaction { * @type {DeleteSet} */ this.deleteSet = new DeleteSet() + /** + * Describes the set of inserted items by ids + * @type {DeleteSet} + */ + this.insertSet = new DeleteSet() /** * Holds the state before the transaction started. - * @type {Map} + * @type {Map?} */ - this.beforeState = getStateVector(doc.store) + this._beforeState = null /** * Holds the state after the transaction. - * @type {Map} + * @type {Map?} */ - this.afterState = new Map() + this._afterState = null /** * All types that were directly modified (property added or child * inserted/deleted). New types are not included in this Set. @@ -119,6 +127,43 @@ export class Transaction { * @type {boolean} */ this._needFormattingCleanup = false + this._done = false + } + + /** + * Holds the state before the transaction started. + * + * @deprecated + * @type {Map} + */ + get beforeState () { + if (this._beforeState == null) { + const sv = getStateVector(this.doc.store) + this.insertSet.clients.forEach((ranges, client) => { + sv.set(client, ranges[0].clock) + }) + this._beforeState = sv + } + return this._beforeState + } + + /** + * Holds the state after the transaction. + * + * @deprecated + * @type {Map} + */ + get afterState () { + if (!this._done) error.unexpectedCase() + if (this._afterState == null) { + const sv = getStateVector(this.doc.store) + this.insertSet.clients.forEach((ranges, client) => { + const d = ranges[ranges.length - 1] + sv.set(client, d.clock + d.len) + }) + this._afterState = sv + } + return this._afterState } } @@ -128,7 +173,7 @@ export class Transaction { * @return {boolean} Whether data was written. */ export const writeUpdateMessageFromTransaction = (encoder, transaction) => { - if (transaction.deleteSet.clients.size === 0 && !map.any(transaction.afterState, (clock, client) => transaction.beforeState.get(client) !== clock)) { + if (transaction.deleteSet.clients.size === 0 && transaction.insertSet.clients.size === 0) { return false } sortAndMergeDeleteSet(transaction.deleteSet) @@ -158,11 +203,28 @@ export const nextID = transaction => { */ export const addChangedTypeToTransaction = (transaction, type, parentSub) => { const item = type._item - if (item === null || (item.id.clock < (transaction.beforeState.get(item.id.client) || 0) && !item.deleted)) { + if (item === null || (!item.deleted && !isDeleted(transaction.insertSet, item.id))) { map.setIfUndefined(transaction.changed, type, set.create).add(parentSub) } } +/** + * @param {Transaction} tr + * @param {AbstractStruct} item + */ +export const addItemToInsertSet = (tr, item) => { + const ranges = map.setIfUndefined(tr.insertSet.clients, item.id.client, () => /** @type {Array} */ ([])) + if (ranges.length > 0) { + const r = ranges[ranges.length - 1] + if (r.clock + r.len === item.id.clock) { + // @ts-ignore + r.len += item.length + return + } + } + ranges.push(new DeleteItem(item.id.clock, item.length)) +} + /** * @param {Array} structs * @param {number} pos @@ -260,13 +322,15 @@ export const tryGc = (ds, store, gcFilter) => { const cleanupTransactions = (transactionCleanups, i) => { if (i < transactionCleanups.length) { const transaction = transactionCleanups[i] + transaction._done = true const doc = transaction.doc const store = doc.store const ds = transaction.deleteSet + const insertSet = transaction.insertSet const mergeStructs = transaction._mergeStructs try { sortAndMergeDeleteSet(ds) - transaction.afterState = getStateVector(transaction.doc.store) + sortAndMergeDeleteSet(insertSet) doc.emit('beforeObserverCalls', [transaction, doc]) /** * An array of event callbacks. @@ -323,15 +387,13 @@ const cleanupTransactions = (transactionCleanups, i) => { tryMergeDeleteSet(ds, store) // on all affected store.clients props, try to merge - transaction.afterState.forEach((clock, client) => { - const beforeClock = transaction.beforeState.get(client) || 0 - if (beforeClock !== clock) { - const structs = /** @type {Array} */ (store.clients.get(client)) - // we iterate from right to left so we can safely remove entries - const firstChangePos = math.max(findIndexSS(structs, beforeClock), 1) - for (let i = structs.length - 1; i >= firstChangePos;) { - i -= 1 + tryToMergeWithLefts(structs, i) - } + transaction.insertSet.clients.forEach((ids, client) => { + const firstClock = ids[0].clock + const structs = /** @type {Array} */ (store.clients.get(client)) + // we iterate from right to left so we can safely remove entries + const firstChangePos = math.max(findIndexSS(structs, firstClock), 1) + for (let i = structs.length - 1; i >= firstChangePos;) { + i -= 1 + tryToMergeWithLefts(structs, i) } }) // try to merge mergeStructs @@ -350,7 +412,7 @@ const cleanupTransactions = (transactionCleanups, i) => { tryToMergeWithLefts(structs, replacedStructPos) } } - if (!transaction.local && transaction.afterState.get(doc.clientID) !== transaction.beforeState.get(doc.clientID)) { + if (!transaction.local && transaction.insertSet.clients.has(doc.clientID)) { logging.print(logging.ORANGE, logging.BOLD, '[yjs] ', logging.UNBOLD, logging.RED, 'Changed the client-id because another client seems to be using it.') doc.clientID = generateNewClientId() } diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 98410e4de..9c2d7f00e 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -226,14 +226,7 @@ export class UndoManager extends ObservableV2 { // neither undoing nor redoing: delete redoStack this.clear(false, true) } - const insertions = new DeleteSet() - transaction.afterState.forEach((endClock, client) => { - const startClock = transaction.beforeState.get(client) || 0 - const len = endClock - startClock - if (len > 0) { - addToDeleteSet(insertions, client, startClock, len) - } - }) + const insertions = transaction.insertSet const now = time.getUnixTime() let didAdd = false if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) { diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 61bc28408..cf47a77fb 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -158,7 +158,7 @@ export class YEvent { * @return {boolean} */ adds (struct) { - return struct.id.clock >= (this.transaction.beforeState.get(struct.id.client) || 0) + return isDeleted(this.transaction.insertSet, struct.id) } /** diff --git a/src/utils/encoding.js b/src/utils/encoding.js index b195ccc3b..9ba8aa27c 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -36,7 +36,8 @@ import { Skip, diffUpdateV2, convertUpdateFormatV2ToV1, - DSDecoderV2, Doc, Transaction, GC, Item, StructStore // eslint-disable-line + DeleteSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line + iterateDeletedStructs } from '../internals.js' import * as encoding from 'lib0/encoding' @@ -101,6 +102,26 @@ export const writeClientsStructs = (encoder, store, _sm) => { }) } +/** + * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder + * @param {StructStore} store + * @param {DeleteSet} idset + * + * @todo at the moment this writes the full deleteset range + * + * @private + * @function + */ +export const writeStructsFromIdSet = (encoder, store, idset) => { + // write # states that were updated + encoding.writeVarUint(encoder.restEncoder, idset.clients.size) + // Write items with higher client ids first + // This heavily improves the conflict algorithm. + array.from(idset.clients.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, ids]) => { + writeStructs(encoder, /** @type {Array} */ (store.clients.get(client)), client, ids[0].clock) + }) +} + /** * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder The decoder object to read data from. * @param {Doc} doc @@ -365,7 +386,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { * @private * @function */ -export const writeStructsFromTransaction = (encoder, transaction) => writeClientsStructs(encoder, transaction.doc.store, transaction.beforeState) +export const writeStructsFromTransaction = (encoder, transaction) => writeStructsFromIdSet(encoder, transaction.doc.store, transaction.insertSet) /** * Read and apply a document update. From c554bd754dcac4f48e327b553bce442e5104b4fb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 8 Apr 2025 14:53:36 +0200 Subject: [PATCH 263/362] add test case for insertSet --- tests/testHelper.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/testHelper.js b/tests/testHelper.js index 0fa651d1a..513f2bc11 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -92,6 +92,11 @@ export class TestYInstance extends Y.Doc { } this.updates.push(update) }) + this.on('afterTransaction', tr => { + if (Array.from(tr.insertSet.clients.values()).some(ids => ids.length !== 1)) { + throw new Error('Currently, we expect that idset contains exactly one item per client.') + } + }) this.connect() } From 46347ee6ec3033f7aa5baa2958dd39100418b62e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 8 Apr 2025 20:50:20 +0200 Subject: [PATCH 264/362] rename DeleteSet=>IdSet and add utilities and perf improvements --- rollup.config.js | 4 +- src/index.js | 11 +- src/internals.js | 2 +- src/structs/ContentDeleted.js | 4 +- src/structs/ContentType.js | 15 +- src/structs/GC.js | 6 +- src/structs/Item.js | 17 +- src/types/YText.js | 8 +- src/utils/AttributionManager.js | 2 +- src/utils/DeleteSet.js | 423 ----------------------------- src/utils/Delta.js | 4 + src/utils/IdSet.js | 462 ++++++++++++++++++++++++++++++++ src/utils/PermanentUserData.js | 25 +- src/utils/Snapshot.js | 56 ++-- src/utils/Transaction.js | 59 ++-- src/utils/UndoManager.js | 26 +- src/utils/YEvent.js | 5 +- src/utils/encoding.js | 11 +- src/utils/updates.js | 24 +- tests/IdSet.tests.js | 195 ++++++++++++++ tests/deleteset.tests.js | 195 -------------- tests/delta.tests.js | 1 - tests/index.js | 4 +- tests/testHelper.js | 6 +- tests/updates.tests.js | 6 +- 25 files changed, 782 insertions(+), 789 deletions(-) delete mode 100644 src/utils/DeleteSet.js create mode 100644 src/utils/IdSet.js create mode 100644 tests/IdSet.tests.js delete mode 100644 tests/deleteset.tests.js diff --git a/rollup.config.js b/rollup.config.js index 144eb6432..e54694fc5 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -17,7 +17,7 @@ export default [{ output: { dir: 'dist', format: 'cjs', - entryFileNames : '[name].cjs', + entryFileNames: '[name].cjs', sourcemap: true }, plugins: [ @@ -34,7 +34,7 @@ export default [{ output: { dir: 'dist', format: 'esm', - entryFileNames : '[name].mjs', + entryFileNames: '[name].mjs', sourcemap: true }, plugins: [ diff --git a/src/index.js b/src/index.js index 5382e376e..54d0b0df1 100644 --- a/src/index.js +++ b/src/index.js @@ -42,8 +42,6 @@ export { getState, Snapshot, createSnapshot, - createDeleteSet, - createDeleteSetFromStructStore, cleanupYTextFormatting, snapshot, emptySnapshot, @@ -56,7 +54,6 @@ export { typeMapGetSnapshot, typeMapGetAllSnapshot, createDocFromSnapshot, - iterateDeletedStructs, applyUpdate, applyUpdateV2, readUpdate, @@ -75,7 +72,6 @@ export { decodeUpdate, decodeUpdateV2, relativePositionToJSON, - isDeleted, isParentOf, equalSnapshots, PermanentUserData, // @TODO experimental @@ -101,9 +97,10 @@ export { UpdateEncoderV2, UpdateDecoderV1, UpdateDecoderV2, - equalDeleteSets, - mergeDeleteSets, - snapshotContainsUpdate + snapshotContainsUpdate, + // idset + equalIdSets, + createDeleteSetFromStructStore } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/internals.js b/src/internals.js index b9fe33bf9..6f9e5de24 100644 --- a/src/internals.js +++ b/src/internals.js @@ -1,5 +1,5 @@ export * from './utils/AbstractConnector.js' -export * from './utils/DeleteSet.js' +export * from './utils/IdSet.js' export * from './utils/Doc.js' export * from './utils/UpdateDecoder.js' export * from './utils/UpdateEncoder.js' diff --git a/src/structs/ContentDeleted.js b/src/structs/ContentDeleted.js index 917ba2474..dc9b776c2 100644 --- a/src/structs/ContentDeleted.js +++ b/src/structs/ContentDeleted.js @@ -1,5 +1,5 @@ import { - addToDeleteSet, + addToIdSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line } from '../internals.js' @@ -63,7 +63,7 @@ export class ContentDeleted { * @param {Item} item */ integrate (transaction, item) { - addToDeleteSet(transaction.deleteSet, item.id.client, item.id.clock, this.len) + addToIdSet(transaction.deleteSet, item.id.client, item.id.clock, this.len) item.markDeleted() } diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index 9785d6749..5f1c71f92 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -6,8 +6,7 @@ import { readYXmlFragment, readYXmlHook, readYXmlText, - isDeleted, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, YEvent, AbstractType // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, AbstractType // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -77,18 +76,18 @@ export class ContentType { } /** - * @param {number} offset + * @param {number} _offset * @return {ContentType} */ - splice (offset) { + splice (_offset) { throw error.methodUnimplemented() } /** - * @param {ContentType} right + * @param {ContentType} _right * @return {boolean} */ - mergeWith (right) { + mergeWith (_right) { return false } @@ -108,7 +107,7 @@ export class ContentType { while (item !== null) { if (!item.deleted) { item.delete(transaction) - } else if (!isDeleted(transaction.insertSet, item.id)) { + } else if (!transaction.insertSet.has(item.id)) { // This will be gc'd later and we want to merge it if possible // We try to merge all deleted items after each transaction, // but we have no knowledge about that this needs to be merged @@ -120,7 +119,7 @@ export class ContentType { this.type._map.forEach(item => { if (!item.deleted) { item.delete(transaction) - } else if (!isDeleted(transaction.insertSet, item.id)) { + } else if (!transaction.insertSet.has(item.id)) { // same as above transaction._mergeStructs.push(item) } diff --git a/src/structs/GC.js b/src/structs/GC.js index cdda47151..28e4e8a1a 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -1,8 +1,8 @@ import { AbstractStruct, addStruct, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID, // eslint-disable-line - addItemToInsertSet + addStructToIdSet, + UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction // eslint-disable-line } from '../internals.js' export const structGCRefNumber = 0 @@ -38,7 +38,7 @@ export class GC extends AbstractStruct { this.id.clock += offset this.length -= offset } - addItemToInsertSet(transaction, this) + addStructToIdSet(transaction.insertSet, this) addStruct(transaction.doc.store, this) } diff --git a/src/structs/Item.js b/src/structs/Item.js index 923db3aac..8ca713c3d 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -4,7 +4,7 @@ import { AbstractStruct, replaceStruct, addStruct, - addToDeleteSet, + addToIdSet, findRootTypeKey, compareIDs, getItem, @@ -21,9 +21,8 @@ import { readContentFormat, readContentType, addChangedTypeToTransaction, - isDeleted, - StackItem, DeleteSet, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line - addItemToInsertSet + addStructToIdSet, + IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -125,7 +124,7 @@ export const splitItem = (transaction, leftItem, diff) => { * @param {Array} stack * @param {ID} id */ -const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => isDeleted(s.deletions, id)) +const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => s.deletions.has(id)) /** * Redoes the effect of this operation. @@ -133,7 +132,7 @@ const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackI * @param {Transaction} transaction The Yjs instance. * @param {Item} item * @param {Set} redoitems - * @param {DeleteSet} itemsToDelete + * @param {IdSet} itemsToDelete * @param {boolean} ignoreRemoteMapChanges * @param {import('../utils/UndoManager.js').UndoManager} um * @@ -211,7 +210,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo left = item // Iterate right while right is in itemsToDelete // If it is intended to delete right while item is redone, we can expect that item should replace right. - while (left !== null && left.right !== null && (left.right.redone || isDeleted(itemsToDelete, left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) { + while (left !== null && left.right !== null && (left.right.redone || itemsToDelete.has(left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) { left = left.right // follow redone while (left.redone) left = getItemCleanStart(transaction, left.redone) @@ -515,7 +514,7 @@ export class Item extends AbstractStruct { if (this.parentSub === null && this.countable && !this.deleted) { /** @type {AbstractType} */ (this.parent)._length += this.length } - addItemToInsertSet(transaction, this) + addStructToIdSet(transaction.insertSet, this) addStruct(transaction.doc.store, this) this.content.integrate(transaction, this) // add parent to transaction.changed @@ -619,7 +618,7 @@ export class Item extends AbstractStruct { parent._length -= this.length } this.markDeleted() - addToDeleteSet(transaction.deleteSet, this.id.client, this.id.clock, this.length) + addToIdSet(transaction.deleteSet, this.id.client, this.id.clock, this.length) addChangedTypeToTransaction(transaction, parent, this.parentSub) this.content.delete(transaction) } diff --git a/src/types/YText.js b/src/types/YText.js index f902cc85b..3e4b4986c 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -17,8 +17,7 @@ import { ContentFormat, ContentString, splitSnapshotAffectedStructs, - iterateDeletedStructs, - iterateStructs, + iterateStructsByIdSet, findMarker, typeMapDelete, typeMapSet, @@ -493,7 +492,7 @@ export const cleanupYTextAfterTransaction = transaction => { const needFullCleanup = new Set() // check if another formatting item was inserted const doc = transaction.doc - iterateDeletedStructs(transaction, transaction.insertSet, (item) => { + iterateStructsByIdSet(transaction, transaction.insertSet, (item) => { if ( !item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC ) { @@ -502,7 +501,7 @@ export const cleanupYTextAfterTransaction = transaction => { }) // cleanup in a new transaction transact(doc, (t) => { - iterateDeletedStructs(transaction, transaction.deleteSet, item => { + iterateStructsByIdSet(transaction, transaction.deleteSet, item => { if (item instanceof GC || !(/** @type {YText} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YText} */ (item.parent))) { return } @@ -1082,7 +1081,6 @@ export class YText extends AbstractType { return d.done() } - /** * Returns the Delta representation of this YText type. * diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 205826c4e..f174e836d 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -1,7 +1,7 @@ export class AttributionManager { /** - * + * */ constructor () { } diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js deleted file mode 100644 index 3b48bb42f..000000000 --- a/src/utils/DeleteSet.js +++ /dev/null @@ -1,423 +0,0 @@ -import { - findIndexSS, - getState, - splitItem, - iterateStructs, - UpdateEncoderV2, - DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line -} from '../internals.js' - -import * as array from 'lib0/array' -import * as math from 'lib0/math' -import * as map from 'lib0/map' -import * as encoding from 'lib0/encoding' -import * as decoding from 'lib0/decoding' - -export class DeleteItem { - /** - * @param {number} clock - * @param {number} len - */ - constructor (clock, len) { - /** - * @readonly - * @type {number} - */ - this.clock = clock - /** - * @readonly - * @type {number} - */ - this.len = len - } -} - -/** - * We no longer maintain a DeleteStore. DeleteSet is a temporary object that is created when needed. - * - When created in a transaction, it must only be accessed after sorting, and merging - * - This DeleteSet is send to other clients - * - We do not create a DeleteSet when we send a sync message. The DeleteSet message is created directly from StructStore - * - We read a DeleteSet as part of a sync/update message. In this case the DeleteSet is already sorted and merged. - */ -export class DeleteSet { - constructor () { - /** - * @type {Map>} - */ - this.clients = new Map() - } -} - -/** - * Iterate over all structs that the DeleteSet gc's. - * - * @param {Transaction} transaction - * @param {DeleteSet} ds - * @param {function(GC|Item):void} f - * - * @function - */ -export const iterateDeletedStructs = (transaction, ds, f) => - ds.clients.forEach((deletes, clientid) => { - const structs = /** @type {Array} */ (transaction.doc.store.clients.get(clientid)) - if (structs != null) { - const lastStruct = structs[structs.length - 1] - const clockState = lastStruct.id.clock + lastStruct.length - for (let i = 0, del = deletes[i]; i < deletes.length && del.clock < clockState; del = deletes[++i]) { - iterateStructs(transaction, structs, del.clock, del.len, f) - } - } - }) - -/** - * @param {Array} dis - * @param {number} clock - * @return {number|null} - * - * @private - * @function - */ -export const findIndexDS = (dis, clock) => { - let left = 0 - let right = dis.length - 1 - while (left <= right) { - const midindex = math.floor((left + right) / 2) - const mid = dis[midindex] - const midclock = mid.clock - if (midclock <= clock) { - if (clock < midclock + mid.len) { - return midindex - } - left = midindex + 1 - } else { - right = midindex - 1 - } - } - return null -} - -/** - * @param {DeleteSet} ds - * @param {ID} id - * @return {boolean} - * - * @private - * @function - */ -export const isDeleted = (ds, id) => { - const dis = ds.clients.get(id.client) - return dis !== undefined && findIndexDS(dis, id.clock) !== null -} - -/** - * @param {DeleteSet} ds - * - * @private - * @function - */ -export const sortAndMergeDeleteSet = ds => { - ds.clients.forEach((dels, client) => { - dels.sort((a, b) => a.clock - b.clock) - // merge items without filtering or splicing the array - // i is the current pointer - // j refers to the current insert position for the pointed item - // try to merge dels[i] into dels[j-1] or set dels[j]=dels[i] - let i, j - for (i = 1, j = 1; i < dels.length; i++) { - const left = dels[j - 1] - const right = dels[i] - if (left.clock + left.len >= right.clock) { - const r = right.clock + right.len - left.clock - if (left.len < r) { - dels[j - 1] = new DeleteItem(left.clock, r) - } - } else if (left.len === 0) { - dels[j - 1] = right - } else { - if (j < i) { - dels[j] = right - } - j++ - } - } - if (dels[j - 1].len === 0) { - dels.length = j - 1 - } else { - dels.length = j - } - if (dels.length === 0) { - ds.clients.delete(client) - } - }) -} - -/** - * @param {Array} dss - * @return {DeleteSet} A fresh DeleteSet - */ -export const mergeDeleteSets = dss => { - const merged = new DeleteSet() - for (let dssI = 0; dssI < dss.length; dssI++) { - dss[dssI].clients.forEach((delsLeft, client) => { - if (!merged.clients.has(client)) { - // Write all missing keys from current ds and all following. - // If merged already contains `client` current ds has already been added. - /** - * @type {Array} - */ - const dels = delsLeft.slice() - for (let i = dssI + 1; i < dss.length; i++) { - array.appendTo(dels, dss[i].clients.get(client) || []) - } - merged.clients.set(client, dels) - } - }) - } - sortAndMergeDeleteSet(merged) - return merged -} - -/** - * Remove all ranges from `exclude` from `ds`. The result will contain all ranges from `ds` that are not - * in `exclude`. - * - * @param {DeleteSet} ds - * @param {DeleteSet} exclude - * @return {DeleteSet} - */ -export const diffDeleteSet = (ds, exclude) => { - const res = new DeleteSet() - ds.clients.forEach((ranges, client) => { - /** - * @type {Array} - */ - const resRanges = [] - const excludedRanges = exclude.clients.get(client) ?? [] - let i = 0, j = 0 - let currRange = ranges[0] - while (i < ranges.length && j < excludedRanges.length) { - const e = excludedRanges[j] - if (currRange.clock + currRange.len <= e.clock) { // no overlapping, use next range item - if (currRange.len > 0) resRanges.push(currRange) - currRange = ranges[++i] - } else if (e.clock + e.len <= currRange.clock) { // no overlapping, use next excluded item - j++ - } else if (e.clock <= currRange.clock) { // exclude laps into range (we already know that the ranges somehow collide) - const newClock = e.clock + e.len - const newLen = currRange.clock + currRange.len - newClock - if (newLen > 0) { - currRange = new DeleteItem(newClock, newLen) - j++ - } else { - // this item is completely overwritten. len=0. We can jump to the next range - currRange = ranges[++i] - } - } else { // currRange.clock < e.clock -- range laps into exclude => adjust len - // beginning can't be empty, add it to the result - const nextLen = e.clock - currRange.clock - resRanges.push(new DeleteItem(currRange.clock, nextLen)) - // retain the remaining length after exclude in currRange - currRange = new DeleteItem(currRange.clock + e.len + nextLen, math.max(currRange.len - e.len - nextLen, 0)) - if (currRange.len === 0) currRange = ranges[++i] - else j++ - } - } - if (currRange != null) { - resRanges.push(currRange) - } - i++ - while (i < ranges.length) { - resRanges.push(ranges[i++]) - } - if (resRanges.length > 0) res.clients.set(client, resRanges) - }) - return res -} - -/** - * @param {DeleteSet} ds - * @param {number} client - * @param {number} clock - * @param {number} length - * - * @private - * @function - */ -export const addToDeleteSet = (ds, client, clock, length) => { - map.setIfUndefined(ds.clients, client, () => /** @type {Array} */ ([])).push(new DeleteItem(clock, length)) -} - -export const createDeleteSet = () => new DeleteSet() - -/** - * @param {StructStore} ss - * @return {DeleteSet} Merged and sorted DeleteSet - * - * @private - * @function - */ -export const createDeleteSetFromStructStore = ss => { - const ds = createDeleteSet() - ss.clients.forEach((structs, client) => { - /** - * @type {Array} - */ - const dsitems = [] - for (let i = 0; i < structs.length; i++) { - const struct = structs[i] - if (struct.deleted) { - const clock = struct.id.clock - let len = struct.length - if (i + 1 < structs.length) { - for (let next = structs[i + 1]; i + 1 < structs.length && next.deleted; next = structs[++i + 1]) { - len += next.length - } - } - dsitems.push(new DeleteItem(clock, len)) - } - } - if (dsitems.length > 0) { - ds.clients.set(client, dsitems) - } - }) - return ds -} - -/** - * @param {DSEncoderV1 | DSEncoderV2} encoder - * @param {DeleteSet} ds - * - * @private - * @function - */ -export const writeDeleteSet = (encoder, ds) => { - encoding.writeVarUint(encoder.restEncoder, ds.clients.size) - - // Ensure that the delete set is written in a deterministic order - array.from(ds.clients.entries()) - .sort((a, b) => b[0] - a[0]) - .forEach(([client, dsitems]) => { - encoder.resetDsCurVal() - encoding.writeVarUint(encoder.restEncoder, client) - const len = dsitems.length - encoding.writeVarUint(encoder.restEncoder, len) - for (let i = 0; i < len; i++) { - const item = dsitems[i] - encoder.writeDsClock(item.clock) - encoder.writeDsLen(item.len) - } - }) -} - -/** - * @param {DSDecoderV1 | DSDecoderV2} decoder - * @return {DeleteSet} - * - * @private - * @function - */ -export const readDeleteSet = decoder => { - const ds = new DeleteSet() - const numClients = decoding.readVarUint(decoder.restDecoder) - for (let i = 0; i < numClients; i++) { - decoder.resetDsCurVal() - const client = decoding.readVarUint(decoder.restDecoder) - const numberOfDeletes = decoding.readVarUint(decoder.restDecoder) - if (numberOfDeletes > 0) { - const dsField = map.setIfUndefined(ds.clients, client, () => /** @type {Array} */ ([])) - for (let i = 0; i < numberOfDeletes; i++) { - dsField.push(new DeleteItem(decoder.readDsClock(), decoder.readDsLen())) - } - } - } - return ds -} - -/** - * @todo YDecoder also contains references to String and other Decoders. Would make sense to exchange YDecoder.toUint8Array for YDecoder.DsToUint8Array().. - */ - -/** - * @param {DSDecoderV1 | DSDecoderV2} decoder - * @param {Transaction} transaction - * @param {StructStore} store - * @return {Uint8Array|null} Returns a v2 update containing all deletes that couldn't be applied yet; or null if all deletes were applied successfully. - * - * @private - * @function - */ -export const readAndApplyDeleteSet = (decoder, transaction, store) => { - const unappliedDS = new DeleteSet() - const numClients = decoding.readVarUint(decoder.restDecoder) - for (let i = 0; i < numClients; i++) { - decoder.resetDsCurVal() - const client = decoding.readVarUint(decoder.restDecoder) - const numberOfDeletes = decoding.readVarUint(decoder.restDecoder) - const structs = store.clients.get(client) || [] - const state = getState(store, client) - for (let i = 0; i < numberOfDeletes; i++) { - const clock = decoder.readDsClock() - const clockEnd = clock + decoder.readDsLen() - if (clock < state) { - if (state < clockEnd) { - addToDeleteSet(unappliedDS, client, state, clockEnd - state) - } - let index = findIndexSS(structs, clock) - /** - * We can ignore the case of GC and Delete structs, because we are going to skip them - * @type {Item} - */ - // @ts-ignore - let struct = structs[index] - // split the first item if necessary - if (!struct.deleted && struct.id.clock < clock) { - structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock)) - index++ // increase we now want to use the next struct - } - while (index < structs.length) { - // @ts-ignore - struct = structs[index++] - if (struct.id.clock < clockEnd) { - if (!struct.deleted) { - if (clockEnd < struct.id.clock + struct.length) { - structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock)) - } - struct.delete(transaction) - } - } else { - break - } - } - } else { - addToDeleteSet(unappliedDS, client, clock, clockEnd - clock) - } - } - } - if (unappliedDS.clients.size > 0) { - const ds = new UpdateEncoderV2() - encoding.writeVarUint(ds.restEncoder, 0) // encode 0 structs - writeDeleteSet(ds, unappliedDS) - return ds.toUint8Array() - } - return null -} - -/** - * @param {DeleteSet} ds1 - * @param {DeleteSet} ds2 - */ -export const equalDeleteSets = (ds1, ds2) => { - if (ds1.clients.size !== ds2.clients.size) return false - for (const [client, deleteItems1] of ds1.clients.entries()) { - const deleteItems2 = /** @type {Array} */ (ds2.clients.get(client)) - if (deleteItems2 === undefined || deleteItems1.length !== deleteItems2.length) return false - for (let i = 0; i < deleteItems1.length; i++) { - const di1 = deleteItems1[i] - const di2 = deleteItems2[i] - if (di1.clock !== di2.clock || di1.len !== di2.len) { - return false - } - } - } - return true -} diff --git a/src/utils/Delta.js b/src/utils/Delta.js index e306ab81b..60f12c4bd 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -27,6 +27,7 @@ export class InsertOp { this.attributes = attributes this.attribution = attribution } + toJSON () { return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) } @@ -39,6 +40,7 @@ export class DeleteOp { constructor (len) { this.delete = len } + toJSON () { return { delete: this.delete } } @@ -55,6 +57,7 @@ export class RetainOp { this.attributes = attributes this.attribution = attribution } + toJSON () { return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {}) } @@ -67,6 +70,7 @@ export class Delta { */ this.ops = [] } + toJSON () { return { ops: this.ops.map(o => o.toJSON()) } } diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js new file mode 100644 index 000000000..099db7ffa --- /dev/null +++ b/src/utils/IdSet.js @@ -0,0 +1,462 @@ +import { + findIndexSS, + getState, + splitItem, + iterateStructs, + UpdateEncoderV2, + AbstractStruct, DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line +} from '../internals.js' + +import * as array from 'lib0/array' +import * as math from 'lib0/math' +import * as encoding from 'lib0/encoding' +import * as decoding from 'lib0/decoding' + +export class IdRange { + /** + * @param {number} clock + * @param {number} len + */ + constructor (clock, len) { + /** + * @readonly + * @type {number} + */ + this.clock = clock + /** + * @readonly + * @type {number} + */ + this.len = len + } +} + +class IdRanges { + /** + * @param {Array} ids + */ + constructor (ids) { + this.sorted = false + /** + * @private + */ + this._ids = ids + } + + /** + * @param {number} clock + * @param {number} length + */ + add (clock, length) { + const last = this._ids[this._ids.length - 1] + if (last.clock + last.len === clock) { + this._ids[this._ids.length - 1] = new IdRange(last.clock, last.len + length) + } else { + this.sorted = false + this._ids.push(new IdRange(clock, length)) + } + } + + /** + * Return the list of id ranges, sorted and merged. + */ + getIds () { + const ids = this._ids + if (!this.sorted) { + this.sorted = true + ids.sort((a, b) => a.clock - b.clock) + // merge items without filtering or splicing the array + // i is the current pointer + // j refers to the current insert position for the pointed item + // try to merge dels[i] into dels[j-1] or set dels[j]=dels[i] + let i, j + for (i = 1, j = 1; i < ids.length; i++) { + const left = ids[j - 1] + const right = ids[i] + if (left.clock + left.len >= right.clock) { + const r = right.clock + right.len - left.clock + if (left.len < r) { + ids[j - 1] = new IdRange(left.clock, r) + } + } else if (left.len === 0) { + ids[j - 1] = right + } else { + if (j < i) { + ids[j] = right + } + j++ + } + } + if (ids[j - 1].len === 0) { + ids.length = j - 1 + } else { + ids.length = j + } + } + return ids + } +} + +export class IdSet { + constructor () { + /** + * @type {Map} + */ + this.clients = new Map() + } + + /** + * @param {ID} id + * @return {boolean} + */ + has (id) { + const dr = this.clients.get(id.client) + if (dr) { + return findIndexInIdRanges(dr.getIds(), id.clock) !== null + } + return false + } +} + +/** + * Iterate over all structs that are mentioned by the IdSet. + * + * @param {Transaction} transaction + * @param {IdSet} ds + * @param {function(GC|Item):void} f + * + * @function + */ +export const iterateStructsByIdSet = (transaction, ds, f) => + ds.clients.forEach((idRanges, clientid) => { + const ranges = idRanges.getIds() + const structs = /** @type {Array} */ (transaction.doc.store.clients.get(clientid)) + if (structs != null) { + for (let i = 0; i < ranges.length; i++) { + const del = ranges[i] + iterateStructs(transaction, structs, del.clock, del.len, f) + } + } + }) + +/** + * @param {Array} dis + * @param {number} clock + * @return {number|null} + * + * @private + * @function + */ +export const findIndexInIdRanges = (dis, clock) => { + let left = 0 + let right = dis.length - 1 + while (left <= right) { + const midindex = math.floor((left + right) / 2) + const mid = dis[midindex] + const midclock = mid.clock + if (midclock <= clock) { + if (clock < midclock + mid.len) { + return midindex + } + left = midindex + 1 + } else { + right = midindex - 1 + } + } + return null +} + +/** + * @param {Array} idSets + * @return {IdSet} A fresh IdSet + */ +export const mergeIdSets = idSets => { + const merged = new IdSet() + for (let dssI = 0; dssI < idSets.length; dssI++) { + idSets[dssI].clients.forEach((rangesLeft, client) => { + if (!merged.clients.has(client)) { + // Write all missing keys from current ds and all following. + // If merged already contains `client` current ds has already been added. + const ids = rangesLeft.getIds().slice() + for (let i = dssI + 1; i < idSets.length; i++) { + const nextIds = idSets[i].clients.get(client) + if (nextIds) { + array.appendTo(ids, nextIds.getIds()) + } + } + merged.clients.set(client, new IdRanges(ids)) + } + }) + } + return merged +} + +/** + * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not + * in `exclude`. + * + * @param {IdSet} idSet + * @param {IdSet} exclude + * @return {IdSet} + */ +export const diffIdSets = (idSet, exclude) => { + const res = new IdSet() + idSet.clients.forEach((_idRanges, client) => { + /** + * @type {Array} + */ + let resRanges = [] + const _excludedRanges = exclude.clients.get(client) + const idRanges = _idRanges.getIds() + if (_excludedRanges == null) { + resRanges = idRanges.slice() + } else { + const excludedRanges = _excludedRanges.getIds() + let i = 0; let j = 0 + let currRange = idRanges[0] + while (i < idRanges.length && j < excludedRanges.length) { + const e = excludedRanges[j] + if (currRange.clock + currRange.len <= e.clock) { // no overlapping, use next range item + if (currRange.len > 0) resRanges.push(currRange) + currRange = idRanges[++i] + } else if (e.clock + e.len <= currRange.clock) { // no overlapping, use next excluded item + j++ + } else if (e.clock <= currRange.clock) { // exclude laps into range (we already know that the ranges somehow collide) + const newClock = e.clock + e.len + const newLen = currRange.clock + currRange.len - newClock + if (newLen > 0) { + currRange = new IdRange(newClock, newLen) + j++ + } else { + // this item is completely overwritten. len=0. We can jump to the next range + currRange = idRanges[++i] + } + } else { // currRange.clock < e.clock -- range laps into exclude => adjust len + // beginning can't be empty, add it to the result + const nextLen = e.clock - currRange.clock + resRanges.push(new IdRange(currRange.clock, nextLen)) + // retain the remaining length after exclude in currRange + currRange = new IdRange(currRange.clock + e.len + nextLen, math.max(currRange.len - e.len - nextLen, 0)) + if (currRange.len === 0) currRange = idRanges[++i] + else j++ + } + } + if (currRange != null) { + resRanges.push(currRange) + } + i++ + while (i < idRanges.length) { + resRanges.push(idRanges[i++]) + } + } + if (resRanges.length > 0) res.clients.set(client, new IdRanges(resRanges)) + }) + return res +} + +/** + * @param {IdSet} idSet + * @param {number} client + * @param {number} clock + * @param {number} length + * + * @private + * @function + */ +export const addToIdSet = (idSet, client, clock, length) => { + const idRanges = idSet.clients.get(client) + if (idRanges) { + idRanges.add(clock, length) + } else { + idSet.clients.set(client, new IdRanges([new IdRange(clock, length)])) + } +} + +/** + * @param {IdSet} idSet + * @param {AbstractStruct} struct + * + * @private + * @function + */ +export const addStructToIdSet = (idSet, struct) => addToIdSet(idSet, struct.id.client, struct.id.clock, struct.length) + +export const createIdSet = () => new IdSet() + +/** + * @param {StructStore} ss + * @return {IdSet} + * + * @private + * @function + */ +export const createDeleteSetFromStructStore = ss => { + const ds = createIdSet() + ss.clients.forEach((structs, client) => { + /** + * @type {Array} + */ + const dsitems = [] + for (let i = 0; i < structs.length; i++) { + const struct = structs[i] + if (struct.deleted) { + const clock = struct.id.clock + let len = struct.length + if (i + 1 < structs.length) { + for (let next = structs[i + 1]; i + 1 < structs.length && next.deleted; next = structs[++i + 1]) { + len += next.length + } + } + dsitems.push(new IdRange(clock, len)) + } + } + if (dsitems.length > 0) { + ds.clients.set(client, new IdRanges(dsitems)) + } + }) + return ds +} + +/** + * @param {DSEncoderV1 | DSEncoderV2} encoder + * @param {IdSet} idSet + * + * @private + * @function + */ +export const writeIdSet = (encoder, idSet) => { + encoding.writeVarUint(encoder.restEncoder, idSet.clients.size) + // Ensure that the delete set is written in a deterministic order + array.from(idSet.clients.entries()) + .sort((a, b) => b[0] - a[0]) + .forEach(([client, _idRanges]) => { + const idRanges = _idRanges.getIds() + encoder.resetDsCurVal() + encoding.writeVarUint(encoder.restEncoder, client) + const len = idRanges.length + encoding.writeVarUint(encoder.restEncoder, len) + for (let i = 0; i < len; i++) { + const item = idRanges[i] + encoder.writeDsClock(item.clock) + encoder.writeDsLen(item.len) + } + }) +} + +/** + * @param {DSDecoderV1 | DSDecoderV2} decoder + * @return {IdSet} + * + * @private + * @function + */ +export const readIdSet = decoder => { + const ds = new IdSet() + const numClients = decoding.readVarUint(decoder.restDecoder) + for (let i = 0; i < numClients; i++) { + decoder.resetDsCurVal() + const client = decoding.readVarUint(decoder.restDecoder) + const numberOfDeletes = decoding.readVarUint(decoder.restDecoder) + if (numberOfDeletes > 0) { + /** + * @type {Array} + */ + const dsRanges = [] + for (let i = 0; i < numberOfDeletes; i++) { + dsRanges.push(new IdRange(decoder.readDsClock(), decoder.readDsLen())) + } + ds.clients.set(client, new IdRanges(dsRanges)) + } + } + return ds +} + +/** + * @todo YDecoder also contains references to String and other Decoders. Would make sense to exchange YDecoder.toUint8Array for YDecoder.DsToUint8Array().. + */ + +/** + * @param {DSDecoderV1 | DSDecoderV2} decoder + * @param {Transaction} transaction + * @param {StructStore} store + * @return {Uint8Array|null} Returns a v2 update containing all deletes that couldn't be applied yet; or null if all deletes were applied successfully. + * + * @private + * @function + */ +export const readAndApplyDeleteSet = (decoder, transaction, store) => { + const unappliedDS = new IdSet() + const numClients = decoding.readVarUint(decoder.restDecoder) + for (let i = 0; i < numClients; i++) { + decoder.resetDsCurVal() + const client = decoding.readVarUint(decoder.restDecoder) + const numberOfDeletes = decoding.readVarUint(decoder.restDecoder) + const structs = store.clients.get(client) || [] + const state = getState(store, client) + for (let i = 0; i < numberOfDeletes; i++) { + const clock = decoder.readDsClock() + const clockEnd = clock + decoder.readDsLen() + if (clock < state) { + if (state < clockEnd) { + addToIdSet(unappliedDS, client, state, clockEnd - state) + } + let index = findIndexSS(structs, clock) + /** + * We can ignore the case of GC and Delete structs, because we are going to skip them + * @type {Item} + */ + // @ts-ignore + let struct = structs[index] + // split the first item if necessary + if (!struct.deleted && struct.id.clock < clock) { + structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock)) + index++ // increase we now want to use the next struct + } + while (index < structs.length) { + // @ts-ignore + struct = structs[index++] + if (struct.id.clock < clockEnd) { + if (!struct.deleted) { + if (clockEnd < struct.id.clock + struct.length) { + structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock)) + } + struct.delete(transaction) + } + } else { + break + } + } + } else { + addToIdSet(unappliedDS, client, clock, clockEnd - clock) + } + } + } + if (unappliedDS.clients.size > 0) { + const ds = new UpdateEncoderV2() + encoding.writeVarUint(ds.restEncoder, 0) // encode 0 structs + writeIdSet(ds, unappliedDS) + return ds.toUint8Array() + } + return null +} + +/** + * @param {IdSet} ds1 + * @param {IdSet} ds2 + */ +export const equalIdSets = (ds1, ds2) => { + if (ds1.clients.size !== ds2.clients.size) return false + for (const [client, _deleteItems1] of ds1.clients.entries()) { + const deleteItems1 = _deleteItems1.getIds() + const deleteItems2 = ds2.clients.get(client)?.getIds() + if (deleteItems2 === undefined || deleteItems1.length !== deleteItems2.length) return false + for (let i = 0; i < deleteItems1.length; i++) { + const di1 = deleteItems1[i] + const di2 = deleteItems2[i] + if (di1.clock !== di2.clock || di1.len !== di2.len) { + return false + } + } + } + return true +} diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js index a5d990b94..683e77d33 100644 --- a/src/utils/PermanentUserData.js +++ b/src/utils/PermanentUserData.js @@ -1,16 +1,15 @@ import { YArray, YMap, - readDeleteSet, - writeDeleteSet, - createDeleteSet, - DSEncoderV1, DSDecoderV1, ID, DeleteSet, YArrayEvent, Transaction, Doc // eslint-disable-line + readIdSet, + writeIdSet, + createIdSet, + mergeIdSets, + DSEncoderV1, DSDecoderV1, ID, IdSet, YArrayEvent, Transaction, Doc // eslint-disable-line } from '../internals.js' import * as decoding from 'lib0/decoding' -import { mergeDeleteSets, isDeleted } from './DeleteSet.js' - export class PermanentUserData { /** * @param {Doc} doc @@ -18,7 +17,7 @@ export class PermanentUserData { */ constructor (doc, storeType = doc.getMap('users')) { /** - * @type {Map} + * @type {Map} */ const dss = new Map() this.yusers = storeType @@ -45,12 +44,12 @@ export class PermanentUserData { event.changes.added.forEach(item => { item.content.getContent().forEach(encodedDs => { if (encodedDs instanceof Uint8Array) { - this.dss.set(userDescription, mergeDeleteSets([this.dss.get(userDescription) || createDeleteSet(), readDeleteSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))])) + this.dss.set(userDescription, mergeIdSets([this.dss.get(userDescription) || createIdSet(), readIdSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))])) } }) }) }) - this.dss.set(userDescription, mergeDeleteSets(ds.map(encodedDs => readDeleteSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))))) + this.dss.set(userDescription, mergeIdSets(ds.map(encodedDs => readIdSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))))) ids.observe(/** @param {YArrayEvent} event */ event => event.changes.added.forEach(item => item.content.getContent().forEach(addClientId)) ) @@ -71,7 +70,7 @@ export class PermanentUserData { * @param {number} clientid * @param {string} userDescription * @param {Object} conf - * @param {function(Transaction, DeleteSet):boolean} [conf.filter] + * @param {function(Transaction, IdSet):boolean} [conf.filter] */ setUserMapping (doc, clientid, userDescription, { filter = () => true } = {}) { const users = this.yusers @@ -99,7 +98,7 @@ export class PermanentUserData { const encoder = new DSEncoderV1() const ds = this.dss.get(userDescription) if (ds) { - writeDeleteSet(encoder, ds) + writeIdSet(encoder, ds) user.get('ds').push([encoder.toUint8Array()]) } } @@ -111,7 +110,7 @@ export class PermanentUserData { const ds = transaction.deleteSet if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) { const encoder = new DSEncoderV1() - writeDeleteSet(encoder, ds) + writeIdSet(encoder, ds) yds.push([encoder.toUint8Array()]) } }) @@ -132,7 +131,7 @@ export class PermanentUserData { */ getUserByDeletedId (id) { for (const [userDescription, ds] of this.dss.entries()) { - if (isDeleted(ds, id)) { + if (ds.has(id)) { return userDescription } } diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index 777cd39d3..a381ed78a 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -1,23 +1,22 @@ import { - isDeleted, createDeleteSetFromStructStore, getStateVector, getItemCleanStart, - iterateDeletedStructs, - writeDeleteSet, + iterateStructsByIdSet, + writeIdSet, writeStateVector, - readDeleteSet, + readIdSet, readStateVector, - createDeleteSet, + createIdSet, createID, getState, findIndexSS, UpdateEncoderV2, applyUpdateV2, LazyStructReader, - equalDeleteSets, - UpdateDecoderV1, UpdateDecoderV2, DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, DeleteSet, Item, // eslint-disable-line - mergeDeleteSets + equalIdSets, + UpdateDecoderV1, UpdateDecoderV2, DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, IdSet, Item, // eslint-disable-line + mergeIdSets } from '../internals.js' import * as map from 'lib0/map' @@ -27,12 +26,12 @@ import * as encoding from 'lib0/encoding' export class Snapshot { /** - * @param {DeleteSet} ds + * @param {IdSet} ds * @param {Map} sv state map */ constructor (ds, sv) { /** - * @type {DeleteSet} + * @type {IdSet} */ this.ds = ds /** @@ -49,11 +48,9 @@ export class Snapshot { * @return {boolean} */ export const equalSnapshots = (snap1, snap2) => { - const ds1 = snap1.ds.clients - const ds2 = snap2.ds.clients const sv1 = snap1.sv const sv2 = snap2.sv - if (sv1.size !== sv2.size || ds1.size !== ds2.size) { + if (sv1.size !== sv2.size) { return false } for (const [key, value] of sv1.entries()) { @@ -61,20 +58,7 @@ export const equalSnapshots = (snap1, snap2) => { return false } } - for (const [client, dsitems1] of ds1.entries()) { - const dsitems2 = ds2.get(client) || [] - if (dsitems1.length !== dsitems2.length) { - return false - } - for (let i = 0; i < dsitems1.length; i++) { - const dsitem1 = dsitems1[i] - const dsitem2 = dsitems2[i] - if (dsitem1.clock !== dsitem2.clock || dsitem1.len !== dsitem2.len) { - return false - } - } - } - return true + return equalIdSets(snap1.ds, snap2.ds) } /** @@ -83,7 +67,7 @@ export const equalSnapshots = (snap1, snap2) => { * @return {Uint8Array} */ export const encodeSnapshotV2 = (snapshot, encoder = new DSEncoderV2()) => { - writeDeleteSet(encoder, snapshot.ds) + writeIdSet(encoder, snapshot.ds) writeStateVector(encoder, snapshot.sv) return encoder.toUint8Array() } @@ -100,7 +84,7 @@ export const encodeSnapshot = snapshot => encodeSnapshotV2(snapshot, new DSEncod * @return {Snapshot} */ export const decodeSnapshotV2 = (buf, decoder = new DSDecoderV2(decoding.createDecoder(buf))) => { - return new Snapshot(readDeleteSet(decoder), readStateVector(decoder)) + return new Snapshot(readIdSet(decoder), readStateVector(decoder)) } /** @@ -110,13 +94,13 @@ export const decodeSnapshotV2 = (buf, decoder = new DSDecoderV2(decoding.createD export const decodeSnapshot = buf => decodeSnapshotV2(buf, new DSDecoderV1(decoding.createDecoder(buf))) /** - * @param {DeleteSet} ds + * @param {IdSet} ds * @param {Map} sm * @return {Snapshot} */ export const createSnapshot = (ds, sm) => new Snapshot(ds, sm) -export const emptySnapshot = createSnapshot(createDeleteSet(), new Map()) +export const emptySnapshot = createSnapshot(createIdSet(), new Map()) /** * @param {Doc} doc @@ -133,7 +117,7 @@ export const snapshot = doc => createSnapshot(createDeleteSetFromStructStore(doc */ export const isVisible = (item, snapshot) => snapshot === undefined ? !item.deleted - : snapshot.sv.has(item.id.client) && (snapshot.sv.get(item.id.client) || 0) > item.id.clock && !isDeleted(snapshot.ds, item.id) + : snapshot.sv.has(item.id.client) && (snapshot.sv.get(item.id.client) || 0) > item.id.clock && !snapshot.ds.has(item.id) /** * @param {Transaction} transaction @@ -149,7 +133,7 @@ export const splitSnapshotAffectedStructs = (transaction, snapshot) => { getItemCleanStart(transaction, createID(client, clock)) } }) - iterateDeletedStructs(transaction, snapshot.ds, _item => {}) + iterateStructsByIdSet(transaction, snapshot.ds, _item => {}) meta.add(snapshot) } } @@ -203,7 +187,7 @@ export const createDocFromSnapshot = (originDoc, snapshot, newDoc = new Doc()) = structs[i].write(encoder, 0) } } - writeDeleteSet(encoder, ds) + writeIdSet(encoder, ds) }) applyUpdateV2(newDoc, encoder.toUint8Array(), 'snapshot') @@ -225,8 +209,8 @@ export const snapshotContainsUpdateV2 = (snapshot, update, YDecoder = UpdateDeco return false } } - const mergedDS = mergeDeleteSets([snapshot.ds, readDeleteSet(updateDecoder)]) - return equalDeleteSets(snapshot.ds, mergedDS) + const mergedDS = mergeIdSets([snapshot.ds, readIdSet(updateDecoder)]) + return equalIdSets(snapshot.ds, mergedDS) } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index e821683a7..d0cf019de 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -1,19 +1,16 @@ import { getState, writeStructsFromTransaction, - writeDeleteSet, - DeleteSet, - sortAndMergeDeleteSet, + writeIdSet, getStateVector, findIndexSS, callEventHandlerListeners, + createIdSet, Item, generateNewClientId, createID, cleanupYTextAfterTransaction, - isDeleted, - UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, // eslint-disable-line - DeleteItem + IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -62,14 +59,12 @@ export class Transaction { this.doc = doc /** * Describes the set of deleted items by ids - * @type {DeleteSet} */ - this.deleteSet = new DeleteSet() + this.deleteSet = createIdSet() /** * Describes the set of inserted items by ids - * @type {DeleteSet} */ - this.insertSet = new DeleteSet() + this.insertSet = createIdSet() /** * Holds the state before the transaction started. * @type {Map?} @@ -140,7 +135,7 @@ export class Transaction { if (this._beforeState == null) { const sv = getStateVector(this.doc.store) this.insertSet.clients.forEach((ranges, client) => { - sv.set(client, ranges[0].clock) + sv.set(client, ranges.getIds()[0].clock) }) this._beforeState = sv } @@ -157,7 +152,8 @@ export class Transaction { if (!this._done) error.unexpectedCase() if (this._afterState == null) { const sv = getStateVector(this.doc.store) - this.insertSet.clients.forEach((ranges, client) => { + this.insertSet.clients.forEach((_ranges, client) => { + const ranges = _ranges.getIds() const d = ranges[ranges.length - 1] sv.set(client, d.clock + d.len) }) @@ -176,9 +172,8 @@ export const writeUpdateMessageFromTransaction = (encoder, transaction) => { if (transaction.deleteSet.clients.size === 0 && transaction.insertSet.clients.size === 0) { return false } - sortAndMergeDeleteSet(transaction.deleteSet) writeStructsFromTransaction(encoder, transaction) - writeDeleteSet(encoder, transaction.deleteSet) + writeIdSet(encoder, transaction.deleteSet) return true } @@ -203,28 +198,11 @@ export const nextID = transaction => { */ export const addChangedTypeToTransaction = (transaction, type, parentSub) => { const item = type._item - if (item === null || (!item.deleted && !isDeleted(transaction.insertSet, item.id))) { + if (item === null || (!item.deleted && !transaction.insertSet.has(item.id))) { map.setIfUndefined(transaction.changed, type, set.create).add(parentSub) } } -/** - * @param {Transaction} tr - * @param {AbstractStruct} item - */ -export const addItemToInsertSet = (tr, item) => { - const ranges = map.setIfUndefined(tr.insertSet.clients, item.id.client, () => /** @type {Array} */ ([])) - if (ranges.length > 0) { - const r = ranges[ranges.length - 1] - if (r.clock + r.len === item.id.clock) { - // @ts-ignore - r.len += item.length - return - } - } - ranges.push(new DeleteItem(item.id.clock, item.length)) -} - /** * @param {Array} structs * @param {number} pos @@ -254,12 +232,13 @@ const tryToMergeWithLefts = (structs, pos) => { } /** - * @param {DeleteSet} ds + * @param {IdSet} ds * @param {StructStore} store * @param {function(Item):boolean} gcFilter */ const tryGcDeleteSet = (ds, store, gcFilter) => { - for (const [client, deleteItems] of ds.clients.entries()) { + for (const [client, _deleteItems] of ds.clients.entries()) { + const deleteItems = _deleteItems.getIds() const structs = /** @type {Array} */ (store.clients.get(client)) for (let di = deleteItems.length - 1; di >= 0; di--) { const deleteItem = deleteItems[di] @@ -282,13 +261,14 @@ const tryGcDeleteSet = (ds, store, gcFilter) => { } /** - * @param {DeleteSet} ds + * @param {IdSet} ds * @param {StructStore} store */ const tryMergeDeleteSet = (ds, store) => { // try to merge deleted / gc'd items // merge from right to left for better efficiency and so we don't miss any merge targets - ds.clients.forEach((deleteItems, client) => { + ds.clients.forEach((_deleteItems, client) => { + const deleteItems = _deleteItems.getIds() const structs = /** @type {Array} */ (store.clients.get(client)) for (let di = deleteItems.length - 1; di >= 0; di--) { const deleteItem = deleteItems[di] @@ -306,7 +286,7 @@ const tryMergeDeleteSet = (ds, store) => { } /** - * @param {DeleteSet} ds + * @param {IdSet} ds * @param {StructStore} store * @param {function(Item):boolean} gcFilter */ @@ -326,11 +306,8 @@ const cleanupTransactions = (transactionCleanups, i) => { const doc = transaction.doc const store = doc.store const ds = transaction.deleteSet - const insertSet = transaction.insertSet const mergeStructs = transaction._mergeStructs try { - sortAndMergeDeleteSet(ds) - sortAndMergeDeleteSet(insertSet) doc.emit('beforeObserverCalls', [transaction, doc]) /** * An array of event callbacks. @@ -388,7 +365,7 @@ const cleanupTransactions = (transactionCleanups, i) => { // on all affected store.clients props, try to merge transaction.insertSet.clients.forEach((ids, client) => { - const firstClock = ids[0].clock + const firstClock = ids.getIds()[0].clock const structs = /** @type {Array} */ (store.clients.get(client)) // we iterate from right to left so we can safely remove entries const firstChangePos = math.max(findIndexSS(structs, firstClock), 1) diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 9c2d7f00e..6fef6aa86 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -1,6 +1,6 @@ import { - mergeDeleteSets, - iterateDeletedStructs, + mergeIdSets, + iterateStructsByIdSet, keepItem, transact, createID, @@ -8,9 +8,7 @@ import { isParentOf, followRedone, getItemCleanStart, - isDeleted, - addToDeleteSet, - YEvent, Transaction, Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line + YEvent, Transaction, Doc, Item, GC, IdSet, AbstractType // eslint-disable-line } from '../internals.js' import * as time from 'lib0/time' @@ -20,8 +18,8 @@ import { ObservableV2 } from 'lib0/observable' export class StackItem { /** - * @param {DeleteSet} deletions - * @param {DeleteSet} insertions + * @param {IdSet} deletions + * @param {IdSet} insertions */ constructor (deletions, insertions) { this.insertions = insertions @@ -38,7 +36,7 @@ export class StackItem { * @param {StackItem} stackItem */ const clearUndoManagerStackItem = (tr, um, stackItem) => { - iterateDeletedStructs(tr, stackItem.deletions, item => { + iterateStructsByIdSet(tr, stackItem.deletions, item => { if (item instanceof Item && um.scope.some(type => type === tr.doc || isParentOf(/** @type {AbstractType} */ (type), item))) { keepItem(item, false) } @@ -72,7 +70,7 @@ const popStackItem = (undoManager, stack, eventType) => { */ const itemsToDelete = [] let performedChange = false - iterateDeletedStructs(transaction, stackItem.insertions, struct => { + iterateStructsByIdSet(transaction, stackItem.insertions, struct => { if (struct instanceof Item) { if (struct.redone !== null) { let { item, diff } = followRedone(store, struct.id) @@ -86,12 +84,12 @@ const popStackItem = (undoManager, stack, eventType) => { } } }) - iterateDeletedStructs(transaction, stackItem.deletions, struct => { + iterateStructsByIdSet(transaction, stackItem.deletions, struct => { if ( struct instanceof Item && scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType} */ (type), struct)) && // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval. - !isDeleted(stackItem.insertions, struct.id) + !stackItem.insertions.has(struct.id) ) { itemsToRedo.add(struct) } @@ -232,8 +230,8 @@ export class UndoManager extends ObservableV2 { if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) { // append change to last stack op const lastOp = stack[stack.length - 1] - lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet]) - lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions]) + lastOp.deletions = mergeIdSets([lastOp.deletions, transaction.deleteSet]) + lastOp.insertions = mergeIdSets([lastOp.insertions, insertions]) } else { // create a new stack op stack.push(new StackItem(transaction.deleteSet, insertions)) @@ -243,7 +241,7 @@ export class UndoManager extends ObservableV2 { this.lastChange = now } // make sure that deleted structs are not gc'd - iterateDeletedStructs(transaction, transaction.deleteSet, /** @param {Item|GC} item */ item => { + iterateStructsByIdSet(transaction, transaction.deleteSet, /** @param {Item|GC} item */ item => { if (item instanceof Item && this.scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType} */ (type), item))) { keepItem(item, true) } diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index cf47a77fb..deffd7ea6 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -1,5 +1,4 @@ import { - isDeleted, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line } from '../internals.js' @@ -78,7 +77,7 @@ export class YEvent { * @return {boolean} */ deletes (struct) { - return isDeleted(this.transaction.deleteSet, struct.id) + return this.transaction.deleteSet.has(struct.id) } /** @@ -158,7 +157,7 @@ export class YEvent { * @return {boolean} */ adds (struct) { - return isDeleted(this.transaction.insertSet, struct.id) + return this.transaction.insertSet.has(struct.id) } /** diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 9ba8aa27c..4d5297ad4 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -20,7 +20,7 @@ import { createID, getStateVector, readAndApplyDeleteSet, - writeDeleteSet, + writeIdSet, createDeleteSetFromStructStore, transact, readItemContent, @@ -36,8 +36,7 @@ import { Skip, diffUpdateV2, convertUpdateFormatV2ToV1, - DeleteSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line - iterateDeletedStructs + IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line } from '../internals.js' import * as encoding from 'lib0/encoding' @@ -105,7 +104,7 @@ export const writeClientsStructs = (encoder, store, _sm) => { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {StructStore} store - * @param {DeleteSet} idset + * @param {IdSet} idset * * @todo at the moment this writes the full deleteset range * @@ -118,7 +117,7 @@ export const writeStructsFromIdSet = (encoder, store, idset) => { // Write items with higher client ids first // This heavily improves the conflict algorithm. array.from(idset.clients.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, ids]) => { - writeStructs(encoder, /** @type {Array} */ (store.clients.get(client)), client, ids[0].clock) + writeStructs(encoder, /** @type {Array} */ (store.clients.get(client)), client, ids.getIds()[0].clock) }) } @@ -524,7 +523,7 @@ export const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(yd */ export const writeStateAsUpdate = (encoder, doc, targetStateVector = new Map()) => { writeClientsStructs(encoder, doc.store, targetStateVector) - writeDeleteSet(encoder, createDeleteSetFromStructStore(doc.store)) + writeIdSet(encoder, createDeleteSetFromStructStore(doc.store)) } /** diff --git a/src/utils/updates.js b/src/utils/updates.js index fc40cd572..ba69132cc 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -24,15 +24,15 @@ import { DSEncoderV2, GC, Item, - mergeDeleteSets, - readDeleteSet, + mergeIdSets, + readIdSet, readItemContent, Skip, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, - writeDeleteSet, + writeIdSet, YXmlElement, YXmlHook } from '../internals.js' @@ -128,7 +128,7 @@ export const logUpdateV2 = (update, YDecoder = UpdateDecoderV2) => { structs.push(curr) } logging.print('Structs: ', structs) - const ds = readDeleteSet(updateDecoder) + const ds = readIdSet(updateDecoder) logging.print('DeleteSet: ', ds) } @@ -152,7 +152,7 @@ export const decodeUpdateV2 = (update, YDecoder = UpdateDecoderV2) => { } return { structs, - ds: readDeleteSet(updateDecoder) + ds: readIdSet(updateDecoder) } } @@ -452,9 +452,9 @@ export const mergeUpdatesV2 = (updates, YDecoder = UpdateDecoderV2, YEncoder = U } finishLazyStructWriting(lazyStructEncoder) - const dss = updateDecoders.map(decoder => readDeleteSet(decoder)) - const ds = mergeDeleteSets(dss) - writeDeleteSet(updateEncoder, ds) + const dss = updateDecoders.map(decoder => readIdSet(decoder)) + const ds = mergeIdSets(dss) + writeIdSet(updateEncoder, ds) return updateEncoder.toUint8Array() } @@ -495,8 +495,8 @@ export const diffUpdateV2 = (update, sv, YDecoder = UpdateDecoderV2, YEncoder = } finishLazyStructWriting(lazyStructWriter) // write ds - const ds = readDeleteSet(decoder) - writeDeleteSet(encoder, ds) + const ds = readIdSet(decoder) + writeIdSet(encoder, ds) return encoder.toUint8Array() } @@ -585,8 +585,8 @@ export const convertUpdateFormat = (update, blockTransformer, YDecoder, YEncoder writeStructToLazyStructWriter(lazyWriter, blockTransformer(curr), 0) } finishLazyStructWriting(lazyWriter) - const ds = readDeleteSet(updateDecoder) - writeDeleteSet(updateEncoder, ds) + const ds = readIdSet(updateDecoder) + writeIdSet(updateEncoder, ds) return updateEncoder.toUint8Array() } diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js new file mode 100644 index 000000000..f036d3642 --- /dev/null +++ b/tests/IdSet.tests.js @@ -0,0 +1,195 @@ +import * as t from 'lib0/testing' +import * as d from '../src/utils/IdSet.js' +import * as prng from 'lib0/prng' +import * as math from 'lib0/math' + +/** + * @param {Array<[number, number, number]>} ops + */ +const simpleConstructIdSet = ops => { + const ds = d.createIdSet() + ops.forEach(op => { + d.addToIdSet(ds, op[0], op[1], op[2]) + }) + return ds +} + +/** + * @param {d.IdSet} idSet1 + * @param {d.IdSet} idSet2 + */ +const compareIdSets = (idSet1, idSet2) => { + if (idSet1.clients.size !== idSet2.clients.size) return false + for (const [client, _items1] of idSet1.clients.entries()) { + const items1 = _items1.getIds() + const items2 = idSet2.clients.get(client)?.getIds() + t.assert(items2 !== undefined && items1.length === items2.length) + for (let i = 0; i < items1.length; i++) { + const di1 = items1[i] + const di2 = /** @type {Array} */ (items2)[i] + t.assert(di1.clock === di2.clock && di1.len === di2.len) + } + } + return true +} + +/** + * @param {t.TestCase} _tc + */ +export const testIdsetMerge = _tc => { + t.group('filter out empty items (1))', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 0]]), + simpleConstructIdSet([]) + ) + }) + t.group('filter out empty items (2))', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 0], [0, 2, 0]]), + simpleConstructIdSet([]) + ) + }) + t.group('filter out empty items (3 - end))', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 1], [0, 2, 0]]), + simpleConstructIdSet([[0, 1, 1]]) + ) + }) + t.group('filter out empty items (4 - middle))', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 1], [0, 2, 0], [0, 3, 1]]), + simpleConstructIdSet([[0, 1, 1], [0, 3, 1]]) + ) + }) + t.group('filter out empty items (5 - beginning))', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 0], [0, 2, 1], [0, 3, 1]]), + simpleConstructIdSet([[0, 2, 1], [0, 3, 1]]) + ) + }) + t.group('merge of overlapping id ranges', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 2], [0, 0, 2]]), + simpleConstructIdSet([[0, 0, 3]]) + ) + }) + t.group('construct without hole', () => { + compareIdSets( + simpleConstructIdSet([[0, 1, 2], [0, 3, 1]]), + simpleConstructIdSet([[0, 1, 3]]) + ) + }) +} + +/** + * @param {t.TestCase} _tc + */ +export const testDiffing = _tc => { + t.group('simple case (1))', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 1], [0, 3, 1]]), + simpleConstructIdSet([[0, 3, 1]]) + ), + simpleConstructIdSet([[0, 1, 1]]) + ) + }) + t.group('subset left', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3]]), + simpleConstructIdSet([[0, 1, 1]]) + ), + simpleConstructIdSet([[0, 2, 2]]) + ) + }) + t.group('subset right', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3]]), + simpleConstructIdSet([[0, 3, 1]]) + ), + simpleConstructIdSet([[0, 1, 2]]) + ) + }) + t.group('subset middle', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3]]), + simpleConstructIdSet([[0, 2, 1]]) + ), + simpleConstructIdSet([[0, 1, 1], [0, 3, 1]]) + ) + }) + t.group('overlapping left', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3]]), + simpleConstructIdSet([[0, 0, 2]]) + ), + simpleConstructIdSet([[0, 2, 2]]) + ) + }) + t.group('overlapping right', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3]]), + simpleConstructIdSet([[0, 3, 5]]) + ), + simpleConstructIdSet([[0, 1, 2]]) + ) + }) + t.group('overlapping completely', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3]]), + simpleConstructIdSet([[0, 0, 5]]) + ), + simpleConstructIdSet([]) + ) + }) + t.group('overlapping into new range', () => { + compareIdSets( + d.diffIdSets( + simpleConstructIdSet([[0, 1, 3], [0, 5, 2]]), + simpleConstructIdSet([[0, 0, 6]]) + ), + simpleConstructIdSet([[0, 6, 1]]) + ) + }) +} + +/** + * @param {prng.PRNG} gen + * @param {number} clients + * @param {number} clockRange (max clock - exclusive - by each client) + */ +const createRandomDiffSet = (gen, clients, clockRange) => { + const maxOpLen = 5 + const numOfOps = math.ceil((clients * clockRange) / maxOpLen) + const ds = d.createIdSet() + for (let i = 0; i < numOfOps; i++) { + const client = prng.uint32(gen, 0, clients - 1) + const clockStart = prng.uint32(gen, 0, clockRange) + const len = prng.uint32(gen, 0, clockRange - clockStart) + d.addToIdSet(ds, client, clockStart, len) + } + if (ds.clients.size === clients && clients > 1 && prng.bool(gen)) { + ds.clients.delete(prng.uint32(gen, 0, clients)) + } + return ds +} + +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDiffing = tc => { + const clients = 4 + const clockRange = 100 + const ds1 = createRandomDiffSet(tc.prng, clients, clockRange) + const ds2 = createRandomDiffSet(tc.prng, clients, clockRange) + const merged = d.mergeIdSets([ds1, ds2]) + const e1 = d.diffIdSets(ds1, ds2) + const e2 = d.diffIdSets(merged, ds2) + compareIdSets(e1, e2) +} diff --git a/tests/deleteset.tests.js b/tests/deleteset.tests.js deleted file mode 100644 index c28e26847..000000000 --- a/tests/deleteset.tests.js +++ /dev/null @@ -1,195 +0,0 @@ -import * as t from 'lib0/testing' -import * as d from '../src/utils/DeleteSet.js' -import * as prng from 'lib0/prng' -import * as math from 'lib0/math' - -/** - * @param {Array<[number, number, number]>} ops - */ -const simpleConstructDs = ops => { - const ds = new d.DeleteSet() - ops.forEach(op => { - d.addToDeleteSet(ds, op[0], op[1], op[2]) - }) - d.sortAndMergeDeleteSet(ds) - return ds -} - -/** - * @param {d.DeleteSet} ds1 - * @param {d.DeleteSet} ds2 - */ -const compareDs = (ds1, ds2) => { - t.assert(ds1.clients.size === ds2.clients.size) - ds1.clients.forEach((ranges1, clientid) => { - const ranges2 = ds2.clients.get(clientid) ?? [] - t.assert(ranges1.length === ranges2?.length) - for (let i = 0; i < ranges1.length; i++) { - const d1 = ranges1[i] - const d2 = ranges2[i] - t.assert(d1.len === d2.len && d1.clock == d2.clock) - } - }) -} - -/** - * @param {t.TestCase} _tc - */ -export const testDeletesetMerge = _tc => { - t.group('filter out empty items (1))', () => { - compareDs( - simpleConstructDs([[0, 1, 0]]), - simpleConstructDs([]) - ) - }) - t.group('filter out empty items (2))', () => { - compareDs( - simpleConstructDs([[0, 1, 0], [0, 2, 0]]), - simpleConstructDs([]) - ) - }) - t.group('filter out empty items (3 - end))', () => { - compareDs( - simpleConstructDs([[0, 1, 1], [0, 2, 0]]), - simpleConstructDs([[0, 1, 1]]) - ) - }) - t.group('filter out empty items (4 - middle))', () => { - compareDs( - simpleConstructDs([[0, 1, 1], [0, 2, 0], [0, 3, 1]]), - simpleConstructDs([[0, 1, 1], [0, 3, 1]]) - ) - }) - t.group('filter out empty items (5 - beginning))', () => { - compareDs( - simpleConstructDs([[0, 1, 0], [0, 2, 1], [0, 3, 1]]), - simpleConstructDs([[0, 2, 1], [0, 3, 1]]) - ) - }) - t.group('merge of overlapping deletes', () => { - compareDs( - simpleConstructDs([[0, 1, 2], [0, 0, 2]]), - simpleConstructDs([[0, 0, 3]]) - ) - }) - t.group('construct without hole', () => { - compareDs( - simpleConstructDs([[0, 1, 2], [0, 3, 1]]), - simpleConstructDs([[0, 1, 3]]) - ) - }) -} - -/** - * @param {t.TestCase} _tc - */ -export const testDeletesetDiffing = _tc => { - t.group('simple case (1))', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 1], [0, 3, 1]]), - simpleConstructDs([[0, 3, 1]]) - ), - simpleConstructDs([[0, 1, 1]]) - ) - }) - t.group('subset left', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3]]), - simpleConstructDs([[0, 1, 1]]) - ), - simpleConstructDs([[0, 2, 2]]) - ) - }) - t.group('subset right', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3]]), - simpleConstructDs([[0, 3, 1]]) - ), - simpleConstructDs([[0, 1, 2]]) - ) - }) - t.group('subset middle', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3]]), - simpleConstructDs([[0, 2, 1]]) - ), - simpleConstructDs([[0, 1, 1], [0, 3, 1]]) - ) - }) - t.group('overlapping left', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3]]), - simpleConstructDs([[0, 0, 2]]) - ), - simpleConstructDs([[0, 2, 2]]) - ) - }) - t.group('overlapping right', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3]]), - simpleConstructDs([[0, 3, 5]]) - ), - simpleConstructDs([[0, 1, 2]]) - ) - }) - t.group('overlapping completely', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3]]), - simpleConstructDs([[0, 0, 5]]) - ), - simpleConstructDs([]) - ) - }) - t.group('overlapping into new range', () => { - compareDs( - d.diffDeleteSet( - simpleConstructDs([[0, 1, 3], [0, 5, 2]]), - simpleConstructDs([[0, 0, 6]]) - ), - simpleConstructDs([[0, 6, 1]]) - ) - }) -} - -/** - * @param {prng.PRNG} gen - * @param {number} clients - * @param {number} clockRange (max clock - exclusive - by each client) - */ -const createRandomDiffSet = (gen, clients, clockRange) => { - const maxOpLen = 5 - const numOfOps = math.ceil((clients * clockRange) / maxOpLen) - const ds = new d.DeleteSet() - for (let i = 0; i < numOfOps; i++) { - const client = prng.uint32(gen, 0, clients - 1) - const clockStart = prng.uint32(gen, 0, clockRange) - const len = prng.uint32(gen, 0, clockRange - clockStart) - d.addToDeleteSet(ds, client, clockStart, len) - } - d.sortAndMergeDeleteSet(ds) - if (ds.clients.size === clients && clients > 1 && prng.bool(gen)) { - ds.clients.delete(prng.uint32(gen, 0, clients)) - } - return ds -} - -/** - * @param {t.TestCase} tc - */ -export const testRepeatRandomDiffing = tc => { - const clients = 4 - const clockRange = 100 - const ds1 = createRandomDiffSet(tc.prng, clients, clockRange) - const ds2 = createRandomDiffSet(tc.prng, clients, clockRange) - const merged = d.mergeDeleteSets([ds1, ds2]) - const e1 = d.diffDeleteSet(ds1, ds2) - const e2 = d.diffDeleteSet(merged, ds2) - compareDs(e1, e2) -} diff --git a/tests/delta.tests.js b/tests/delta.tests.js index 01cd08d28..eac5b6f12 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -8,4 +8,3 @@ 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' } }]) } - diff --git a/tests/index.js b/tests/index.js index 6e7dc6de0..0103bc09f 100644 --- a/tests/index.js +++ b/tests/index.js @@ -12,7 +12,7 @@ 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 * as deleteset from './deleteset.tests.js' +import * as idset from './IdSet.tests.js' import { runTests } from 'lib0/testing' import { isBrowser, isNode } from 'lib0/environment' @@ -23,7 +23,7 @@ if (isBrowser) { } const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, deleteset + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset } const run = async () => { diff --git a/tests/testHelper.js b/tests/testHelper.js index 513f2bc11..921cadad2 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -6,6 +6,7 @@ import * as syncProtocol from 'y-protocols/sync' import * as object from 'lib0/object' import * as map from 'lib0/map' import * as Y from '../src/index.js' + export * from '../src/index.js' if (typeof window !== 'undefined') { @@ -93,7 +94,8 @@ export class TestYInstance extends Y.Doc { this.updates.push(update) }) this.on('afterTransaction', tr => { - if (Array.from(tr.insertSet.clients.values()).some(ids => ids.length !== 1)) { + // @ts-ignore + if (Array.from(tr.insertSet.clients.values()).some(ids => ids._ids.length !== 1)) { throw new Error('Currently, we expect that idset contains exactly one item per client.') } }) @@ -360,7 +362,7 @@ export const compare = users => { return true }) t.compare(Y.encodeStateVector(users[i]), Y.encodeStateVector(users[i + 1])) - Y.equalDeleteSets(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store)) + Y.equalIdSets(Y.createDeleteSetFromStructStore(users[i].store), Y.createDeleteSetFromStructStore(users[i + 1].store)) compareStructStores(users[i].store, users[i + 1].store) t.compare(Y.encodeSnapshot(Y.snapshot(users[i])), Y.encodeSnapshot(Y.snapshot(users[i + 1]))) } diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 12ac149e6..d517fb928 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -1,7 +1,7 @@ import * as t from 'lib0/testing' 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 { readClientsStructRefs, readIdSet, UpdateDecoderV2, UpdateEncoderV2, writeIdSet } from '../src/internals.js' import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' import * as object from 'lib0/object' @@ -193,10 +193,10 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { const decoder = decoding.createDecoder(diffed) const updateDecoder = new UpdateDecoderV2(decoder) readClientsStructRefs(updateDecoder, new Y.Doc()) - const ds = readDeleteSet(updateDecoder) + const ds = readIdSet(updateDecoder) const updateEncoder = new UpdateEncoderV2() encoding.writeVarUint(updateEncoder.restEncoder, 0) // 0 structs - writeDeleteSet(updateEncoder, ds) + writeIdSet(updateEncoder, ds) const deletesUpdate = updateEncoder.toUint8Array() const mergedDeletes = Y.mergeUpdatesV2([deletesUpdate, partMerged]) if (!hasDeletes || enc !== encDoc) { From 6360297e33f730289f5abbd42c43f36a7c35e22a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 10 Apr 2025 21:07:59 +0200 Subject: [PATCH 265/362] doc maintains ds --- src/index.js | 1 + src/structs/GC.js | 2 ++ src/utils/IdSet.js | 19 +++++++++++++++++++ src/utils/StructStore.js | 4 +++- src/utils/Transaction.js | 4 +++- tests/IdSet.tests.js | 20 +------------------- tests/testHelper.js | 23 +++++++++++++++++++++++ 7 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/index.js b/src/index.js index 54d0b0df1..eba8727b8 100644 --- a/src/index.js +++ b/src/index.js @@ -99,6 +99,7 @@ export { UpdateDecoderV2, snapshotContainsUpdate, // idset + IdSet, equalIdSets, createDeleteSetFromStructStore } from './internals.js' diff --git a/src/structs/GC.js b/src/structs/GC.js index 28e4e8a1a..710751cb5 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -2,6 +2,7 @@ import { AbstractStruct, addStruct, addStructToIdSet, + addToIdSet, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction // eslint-disable-line } from '../internals.js' @@ -38,6 +39,7 @@ export class GC extends AbstractStruct { this.id.clock += offset this.length -= offset } + addToIdSet(transaction.deleteSet, this.id.client, this.id.clock, this.length) addStructToIdSet(transaction.insertSet, this) addStruct(transaction.doc.store, this) } diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 099db7ffa..6135bf205 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -191,6 +191,25 @@ export const mergeIdSets = idSets => { return merged } +/** + * @param {IdSet} dest + * @param {IdSet} src + */ +export const insertIntoIdSet = (dest, src) => { + src.clients.forEach((srcRanges, client) => { + const targetRanges = dest.clients.get(client) + if (targetRanges) { + array.appendTo(targetRanges.getIds(), srcRanges.getIds()) + targetRanges.sorted = false + } else { + const res = new IdRanges(srcRanges.getIds().slice()) + res.sorted = true + dest.clients.set(client, res) + } + }) +} + + /** * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not * in `exclude`. diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 692743d7b..fa4cd23c1 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -1,7 +1,8 @@ import { GC, splitItem, - Transaction, ID, Item, DSDecoderV2 // eslint-disable-line + IdSet, + Transaction, ID, Item // eslint-disable-line } from '../internals.js' import * as math from 'lib0/math' @@ -13,6 +14,7 @@ export class StructStore { * @type {Map>} */ this.clients = new Map() + this.ds = new IdSet() /** * @type {null | { missing: Map, update: Uint8Array }} */ diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index d0cf019de..feebe5baa 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -10,7 +10,8 @@ import { generateNewClientId, createID, cleanupYTextAfterTransaction, - IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line + IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, // eslint-disable-line + insertIntoIdSet } from '../internals.js' import * as error from 'lib0/error' @@ -307,6 +308,7 @@ const cleanupTransactions = (transactionCleanups, i) => { const store = doc.store const ds = transaction.deleteSet const mergeStructs = transaction._mergeStructs + insertIntoIdSet(store.ds, ds) try { doc.emit('beforeObserverCalls', [transaction, doc]) /** diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index f036d3642..c9ca56176 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -2,6 +2,7 @@ import * as t from 'lib0/testing' import * as d from '../src/utils/IdSet.js' import * as prng from 'lib0/prng' import * as math from 'lib0/math' +import { compareIdSets } from './testHelper.js' /** * @param {Array<[number, number, number]>} ops @@ -14,25 +15,6 @@ const simpleConstructIdSet = ops => { return ds } -/** - * @param {d.IdSet} idSet1 - * @param {d.IdSet} idSet2 - */ -const compareIdSets = (idSet1, idSet2) => { - if (idSet1.clients.size !== idSet2.clients.size) return false - for (const [client, _items1] of idSet1.clients.entries()) { - const items1 = _items1.getIds() - const items2 = idSet2.clients.get(client)?.getIds() - t.assert(items2 !== undefined && items1.length === items2.length) - for (let i = 0; i < items1.length; i++) { - const di1 = items1[i] - const di2 = /** @type {Array} */ (items2)[i] - t.assert(di1.clock === di2.clock && di1.len === di2.len) - } - } - return true -} - /** * @param {t.TestCase} _tc */ diff --git a/tests/testHelper.js b/tests/testHelper.js index 921cadad2..60c323f75 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -304,6 +304,26 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => { return /** @type {any} */ (result) } +/** + * @param {Y.IdSet} idSet1 + * @param {Y.IdSet} idSet2 + */ +export const compareIdSets = (idSet1, idSet2) => { + if (idSet1.clients.size !== idSet2.clients.size) return false + for (const [client, _items1] of idSet1.clients.entries()) { + const items1 = _items1.getIds() + const items2 = idSet2.clients.get(client)?.getIds() + t.assert(items2 !== undefined && items1.length === items2.length) + for (let i = 0; i < items1.length; i++) { + const di1 = items1[i] + const di2 = /** @type {Array} */ (items2)[i] + t.assert(di1.clock === di2.clock && di1.len === di2.len) + } + } + return true +} + + /** * 1. reconnect and flush all * 2. user 0 gc @@ -366,6 +386,9 @@ export const compare = users => { compareStructStores(users[i].store, users[i + 1].store) t.compare(Y.encodeSnapshot(Y.snapshot(users[i])), Y.encodeSnapshot(Y.snapshot(users[i + 1]))) } + users.forEach(user => { + compareIdSets(user.store.ds, Y.createDeleteSetFromStructStore(user.store)) + }) users.map(u => u.destroy()) } From 869dd2aa41af8fe2811b759f3cf2887f0713a2ac Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 10 Apr 2025 21:19:30 +0200 Subject: [PATCH 266/362] use stores ds to compute state as update --- src/utils/encoding.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 4d5297ad4..097d96e8e 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -21,7 +21,6 @@ import { getStateVector, readAndApplyDeleteSet, writeIdSet, - createDeleteSetFromStructStore, transact, readItemContent, UpdateDecoderV1, @@ -523,7 +522,7 @@ export const applyUpdate = (ydoc, update, transactionOrigin) => applyUpdateV2(yd */ export const writeStateAsUpdate = (encoder, doc, targetStateVector = new Map()) => { writeClientsStructs(encoder, doc.store, targetStateVector) - writeIdSet(encoder, createDeleteSetFromStructStore(doc.store)) + writeIdSet(encoder, doc.store.ds) } /** From a6ae65d32c9f01c2761343ee17b8ecc29497ffa0 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 12 Apr 2025 14:44:37 +0200 Subject: [PATCH 267/362] Work on AttributionManager --- package-lock.json | 8 +- package.json | 2 +- src/index.js | 4 +- src/utils/AttributionManager.js | 252 +++++++++++++++++++++++++++++- src/utils/IdSet.js | 8 +- tests/AttributionManager.tests.js | 131 ++++++++++++++++ tests/IdSet.tests.js | 31 +++- tests/index.js | 3 +- tests/testHelper.js | 22 +++ 9 files changed, 444 insertions(+), 17 deletions(-) create mode 100644 tests/AttributionManager.tests.js diff --git a/package-lock.json b/package-lock.json index 1d07c1782..1f9e9fb2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.6.27", "license": "MIT", "dependencies": { - "lib0": "^0.2.101", + "lib0": "^0.2.103", "y-protocols": "^1.0.5" }, "devDependencies": { @@ -2773,9 +2773,9 @@ } }, "node_modules/lib0": { - "version": "0.2.101", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.101.tgz", - "integrity": "sha512-LljA6+Ehf0Z7YnxhgSAvspzWALjW4wlWdN/W4iGiqYc1KvXQgOVXWI0xwlwqozIL5WRdKeUW2gq0DLhFsY+Xlw==", + "version": "0.2.103", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.103.tgz", + "integrity": "sha512-1zT9KqSh54uEQZksnm8ONj0bclW3PrisT59nhgY2eOV4PaCZ5Pt9MV4y4KGkNIE/5vp6yNzpYX/+5/aGvfZS5Q==", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" diff --git a/package.json b/package.json index 448c39be8..fd761b8f8 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.101", + "lib0": "^0.2.103", "y-protocols": "^1.0.5" }, "devDependencies": { diff --git a/src/index.js b/src/index.js index eba8727b8..5adfac567 100644 --- a/src/index.js +++ b/src/index.js @@ -101,7 +101,9 @@ export { // idset IdSet, equalIdSets, - createDeleteSetFromStructStore + createDeleteSetFromStructStore, + AttributionManager, + createAttributionManager } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index f174e836d..b06023e8a 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -1,8 +1,256 @@ +import { + findIndexInIdRanges, + ID // @eslint-disable-line +} from '../internals.js' -export class AttributionManager { +import * as array from 'lib0/array' + +/** + * @template T + * @param {Array} attrs + * @param {T} attr + * + */ +const amAttrsHas = (attrs, attr) => attrs.find(a => a === attr) + +/** + * @template T + * @param {Array} a + * @param {Array} b + */ +export const amAttrsEqual = (a, b) => a.length === b.length && a.every(v => amAttrsHas(b, v)) + +/** + * @template T + * @param {Array} a + * @param {Array} b + */ +const amAttrRangeJoin = (a, b) => a.concat(b.filter(attr => !amAttrsHas(a, attr))) + +/** + * @template Attrs + */ +export class AttrRange { + /** + * @param {number} clock + * @param {number} len + * @param {Array} attrs + */ + constructor (clock, len, attrs) { + /** + * @readonly + */ + this.clock = clock + /** + * @readonly + */ + this.len = len + /** + * @readonly + */ + this.attrs = attrs + } +} + +/** + * @template Attrs + */ +class AttrRanges { /** - * + * @param {Array>} ids */ + constructor (ids) { + this.sorted = false + /** + * @private + */ + this._ids = ids + } + + /** + * @param {number} clock + * @param {number} length + * @param {Array} attrs + */ + add (clock, length, attrs) { + this.sorted = false + this._ids.push(new AttrRange(clock, length, attrs)) + } + + /** + * Return the list of id ranges, sorted and merged. + */ + getIds () { + const ids = this._ids + if (!this.sorted) { + this.sorted = true + ids.sort((a, b) => a.clock - b.clock) + /** + * algorithm thoughts: + * - sort (by clock AND by length), bigger length is to the right (or not, we can't make + * assumptions abouth length after long length has been split) + * -- maybe better: sort by clock+length. Then split items from right to left. This way, items are always + * in the right order. But I also need to swap if left items is smaller after split + * --- thought: there is no way to go around swapping. Unless, for each item from left to + * right, when I have to split because one of the look-ahead items is overlapping, i split + * it and merge the attributes into the following ones (that I also need to split). Best is + * probably left to right with lookahead. + * - left to right, split overlapping items so that we can make the assumption that either an + * item is overlapping with the next 1-on-1 or it is not overlapping at all (when splitting, + * we can already incorporate the attributes) + * -- better: for each item, go left to right and add own attributes to overlapping items. + * Split them if necessary. After split, i must insert the retainer at a valid position. + * - merge items if neighbor has same attributes + */ + for (let i = 0; i < ids.length - 1; i++) { + const range = ids[i] + const nextRange = ids[i+1] + // find out how to split range. it must match with next range. + // 1) we have space. Split if necessary. + // 2) concat attributes in range to the next range. Split range and splice the remainder at + // the correct position. + if (range.clock < nextRange.clock) { // might need to split range + if (range.clock + range.len > nextRange.clock) { + // is overlapping + const diff = nextRange.clock - range.clock + ids[i] = new AttrRange(range.clock, diff, range.attrs) + ids.splice(i + 1, 0, new AttrRange(nextRange.clock, range.len - diff, range.attrs)) + } + continue + } + // now we know that range.clock === nextRange.clock + // merge range with nextRange + const largerRange = range.len > nextRange.len ? range : nextRange + const smallerLen = range.len < nextRange.len ? range.len : nextRange.len + ids[i] = new AttrRange(range.clock, smallerLen, amAttrRangeJoin(range.attrs, nextRange.attrs)) + if (range.len === nextRange.len) { + ids.splice(i + 1, 1) + i-- + } else { + ids[i + 1] = new AttrRange(range.clock + smallerLen, largerRange.len - smallerLen, largerRange.attrs) + array.bubblesortItem(ids, i + 1, (a, b) => a.clock - b.clock) + } + } + + // merge items without filtering or splicing the array. + // i is the current pointer + // j refers to the current insert position for the pointed item + // try to merge dels[i] into dels[j-1] or set dels[j]=dels[i] + let i, j + for (i = 1, j = 1; i < ids.length; i++) { + const left = ids[j - 1] + const right = ids[i] + if (left.clock + left.len === right.clock && amAttrsEqual(left.attrs, right.attrs)) { + ids[j - 1] = new AttrRange(left.clock, left.len + right.len, left.attrs) + } else if (right.len !== 0) { + if (j < i) { + ids[j] = right + } + j++ + } + } + ids.length = ids[j - 1].len === 0 ? j - 1 : j + } + return ids + } +} + +/** + * @template T + * @param {Array>} ams + * @return {AttributionManager} A fresh IdSet + */ +export const mergeAttributionManagers = ams => { + const merged = createAttributionManager() + for (let amsI = 0; amsI < ams.length; amsI++) { + ams[amsI].clients.forEach((rangesLeft, client) => { + if (!merged.clients.has(client)) { + // Write all missing keys from current set and all following. + // If merged already contains `client` current ds has already been added. + const ids = rangesLeft.getIds().slice() + for (let i = amsI + 1; i < ams.length; i++) { + const nextIds = ams[i].clients.get(client) + if (nextIds) { + array.appendTo(ids, nextIds.getIds()) + } + } + merged.clients.set(client, new AttrRanges(ids)) + } + }) + } + return merged +} + +/** + * @template Attrs + */ +export class AttributionManager { constructor () { + /** + * @type {Map>} + */ + this.clients = new Map() + } + + /** + * @param {ID} id + * @return {boolean} + */ + has (id) { + const dr = this.clients.get(id.client) + if (dr) { + return findIndexInIdRanges(dr.getIds(), id.clock) !== null + } + return false + } + + /** + * @param {ID} id + * @param {number} len + * @return {Array>?} + */ + slice (id, len) { + const dr = this.clients.get(id.client) + if (dr) { + /** + * @type {Array>} + */ + const ranges = dr.getIds() + let index = findIndexInIdRanges(ranges, id.clock) + if (index !== null) { + const res = [] + while (true) { + let r = ranges[index] + if (r.clock < id.clock) { + r = new AttrRange(id.clock, r.len - (id.clock - r.clock), r.attrs) + } + if (r.clock + r.len > id.clock + len) { + r = new AttrRange(r.clock, id.clock + len - r.clock, r.attrs) + } + if (r.len <= 0) break + res.push(r) + index++ + } + return res + } + } + return null + } + + /** + * @param {number} client + * @param {number} clock + * @param {number} len + * @param {Array} attrs + */ + add (client, clock, len, attrs) { + const ranges = this.clients.get(client) + if (ranges == null) { + this.clients.set(client, new AttrRanges([new AttrRange(clock, len, attrs)])) + } else { + ranges.add(clock, len, attrs) + } } } + +export const createAttributionManager = () => new AttributionManager() diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 6135bf205..606386707 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -19,12 +19,10 @@ export class IdRange { */ constructor (clock, len) { /** - * @readonly * @type {number} */ this.clock = clock /** - * @readonly * @type {number} */ this.len = len @@ -87,11 +85,7 @@ class IdRanges { j++ } } - if (ids[j - 1].len === 0) { - ids.length = j - 1 - } else { - ids.length = j - } + ids.length = ids[j - 1].len === 0 ? j - 1 : j } return ids } diff --git a/tests/AttributionManager.tests.js b/tests/AttributionManager.tests.js new file mode 100644 index 000000000..1a9cce9c9 --- /dev/null +++ b/tests/AttributionManager.tests.js @@ -0,0 +1,131 @@ +import * as t from 'lib0/testing' +import * as am from '../src/utils/AttributionManager.js' +import * as prng from 'lib0/prng' +import * as math from 'lib0/math' +import { compareAttributionManagers, createAttributionManager, ID } from './testHelper.js' + +/** + * @template T + * @param {Array<[number, number, number, Array]>} ops + */ +const simpleConstructAttrs = ops => { + const attrs = createAttributionManager() + ops.forEach(op => { + attrs.add(op[0], op[1], op[2], op[3]) + }) + return attrs +} + +/** + * @template T + * @param {prng.PRNG} gen + * @param {number} clients + * @param {number} clockRange (max clock - exclusive - by each client) + * @param {Array} attrChoices (max clock - exclusive - by each client) + * @return {am.AttributionManager} + */ +const createRandomAttributionManager = (gen, clients, clockRange, attrChoices) => { + const maxOpLen = 5 + const numOfOps = math.ceil((clients * clockRange) / maxOpLen) + const attrMngr = createAttributionManager() + for (let i = 0; i < numOfOps; i++) { + const client = prng.uint32(gen, 0, clients - 1) + const clockStart = prng.uint32(gen, 0, clockRange) + const len = prng.uint32(gen, 0, clockRange - clockStart) + const attrs = [prng.oneOf(gen, attrChoices)] + if (prng.bool(gen)) { + attrs.push(prng.oneOf(gen, attrChoices)) + } + attrMngr.add(client, clockStart, len, attrs) + } + return attrMngr +} + +/** + * @param {t.TestCase} _tc + */ +export const testAmMerge = _tc => { + const attrs = [42] + t.group('filter out empty items (1))', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 0, attrs]]), + simpleConstructAttrs([]) + ) + }) + t.group('filter out empty items (2))', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 0, attrs], [0, 2, 0, attrs]]), + simpleConstructAttrs([]) + ) + }) + t.group('filter out empty items (3 - end))', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 1, attrs], [0, 2, 0, attrs]]), + simpleConstructAttrs([[0, 1, 1, attrs]]) + ) + }) + t.group('filter out empty items (4 - middle))', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 1, attrs], [0, 2, 0, attrs], [0, 3, 1, attrs]]), + simpleConstructAttrs([[0, 1, 1, attrs], [0, 3, 1, attrs]]) + ) + }) + t.group('filter out empty items (5 - beginning))', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 0, attrs], [0, 2, 1, attrs], [0, 3, 1, attrs]]), + simpleConstructAttrs([[0, 2, 1, attrs], [0, 3, 1, attrs]]) + ) + }) + t.group('merge of overlapping id ranges', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 2, attrs], [0, 0, 2, attrs]]), + simpleConstructAttrs([[0, 0, 3, attrs]]) + ) + }) + t.group('construct without hole', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 2, attrs], [0, 3, 1, attrs]]), + simpleConstructAttrs([[0, 1, 3, attrs]]) + ) + }) + t.group('no merge of overlapping id ranges with different attributes', () => { + compareAttributionManagers( + simpleConstructAttrs([[0, 1, 2, [1]], [0, 0, 2, [2]]]), + simpleConstructAttrs([[0, 0, 1, [2]], [0, 1, 1, [1, 2]], [0, 2, 1, [2]]]) + ) + }) +} + +/** + * @param {t.TestCase} tc + */ +export const testRepeatMergingMultipleAttrManagers = tc => { + const clients = 4 + const clockRange = 100 + /** + * @type {Array>} + */ + const sets = [] + for (let i = 0; i < 3; i++) { + sets.push(createRandomAttributionManager(tc.prng, clients, clockRange, [1, 2, 3])) + } + const merged = am.mergeAttributionManagers(sets) + const mergedReverse = am.mergeAttributionManagers(sets.reverse()) + compareAttributionManagers(merged, mergedReverse) + const composed = am.createAttributionManager() + for (let iclient = 0; iclient < clients; iclient++) { + for (let iclock = 0; iclock < clockRange + 42; iclock++) { + const mergedHas = merged.has(new ID(iclient, iclock)) + const oneHas = sets.some(ids => ids.has(new ID(iclient, iclock))) + t.assert(mergedHas === oneHas) + const mergedAttrs = merged.slice(new ID(iclient, iclock), 1) + if (mergedAttrs) { + mergedAttrs.forEach(a => { + composed.add(iclient, a.clock, a.len, a.attrs) + }) + } + } + } + compareAttributionManagers(merged, composed) +} + diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index c9ca56176..4601ad1fb 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -2,7 +2,7 @@ import * as t from 'lib0/testing' import * as d from '../src/utils/IdSet.js' import * as prng from 'lib0/prng' import * as math from 'lib0/math' -import { compareIdSets } from './testHelper.js' +import { compareIdSets, ID } from './testHelper.js' /** * @param {Array<[number, number, number]>} ops @@ -175,3 +175,32 @@ export const testRepeatRandomDiffing = tc => { const e2 = d.diffIdSets(merged, ds2) compareIdSets(e1, e2) } + +/** + * @param {t.TestCase} tc + */ +export const testRepeatMergingMultipleIdsets = tc => { + const clients = 4 + const clockRange = 100 + /** + * @type {Array} + */ + const idss = [] + for (let i = 0; i < 3; i++) { + idss.push(createRandomDiffSet(tc.prng, clients, clockRange)) + } + const merged = d.mergeIdSets(idss) + const mergedReverse = d.mergeIdSets(idss.reverse()) + compareIdSets(merged, mergedReverse) + const composed = d.createIdSet() + for (let iclient = 0; iclient < clients; iclient++) { + for (let iclock = 0; iclock < clockRange + 42; iclock++) { + const mergedHas = merged.has(new ID(iclient, iclock)) + const oneHas = idss.some(ids => ids.has(new ID(iclient, iclock))) + t.assert(mergedHas === oneHas) + d.addToIdSet(composed, iclient, iclock, 1) + } + } + compareIdSets(merged, composed) +} + diff --git a/tests/index.js b/tests/index.js index 0103bc09f..fd129a27c 100644 --- a/tests/index.js +++ b/tests/index.js @@ -13,6 +13,7 @@ import * as updates from './updates.tests.js' import * as relativePositions from './relativePositions.tests.js' import * as delta from './delta.tests.js' import * as idset from './IdSet.tests.js' +import * as attributionManager from './AttributionManager.tests.js' import { runTests } from 'lib0/testing' import { isBrowser, isNode } from 'lib0/environment' @@ -23,7 +24,7 @@ if (isBrowser) { } const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset, attributionManager } const run = async () => { diff --git a/tests/testHelper.js b/tests/testHelper.js index 60c323f75..acf38b932 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -6,6 +6,7 @@ import * as syncProtocol from 'y-protocols/sync' import * as object from 'lib0/object' import * as map from 'lib0/map' import * as Y from '../src/index.js' +import { amAttrsEqual } from '../src/internals.js' export * from '../src/index.js' @@ -323,6 +324,27 @@ export const compareIdSets = (idSet1, idSet2) => { return true } +/** + * @template T + * @param {Y.AttributionManager} am1 + * @param {Y.AttributionManager} am2 + */ +export const compareAttributionManagers = (am1, am2) => { + if (am1.clients.size !== am2.clients.size) return false + for (const [client, _items1] of am1.clients.entries()) { + const items1 = _items1.getIds() + const items2 = am2.clients.get(client)?.getIds() + t.assert(items2 !== undefined && items1.length === items2.length) + for (let i = 0; i < items1.length; i++) { + const di1 = items1[i] + const di2 = /** @type {Array>} */ (items2)[i] + t.assert(di1.clock === di2.clock && di1.len === di2.len && amAttrsEqual(di1.attrs, di2.attrs)) + } + } + return true +} + + /** * 1. reconnect and flush all From 8908bd21dcf57cc80baf31cfa1568fe4e4be2066 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 12 Apr 2025 16:12:00 +0200 Subject: [PATCH 268/362] [am] fixed tests --- package-lock.json | 8 ++++---- package.json | 2 +- src/utils/AttributionManager.js | 13 ++++++++----- tests/AttributionManager.tests.js | 10 +++++++--- tests/IdSet.tests.js | 4 +++- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1f9e9fb2a..1388ff884 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.6.27", "license": "MIT", "dependencies": { - "lib0": "^0.2.103", + "lib0": "^0.2.104", "y-protocols": "^1.0.5" }, "devDependencies": { @@ -2773,9 +2773,9 @@ } }, "node_modules/lib0": { - "version": "0.2.103", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.103.tgz", - "integrity": "sha512-1zT9KqSh54uEQZksnm8ONj0bclW3PrisT59nhgY2eOV4PaCZ5Pt9MV4y4KGkNIE/5vp6yNzpYX/+5/aGvfZS5Q==", + "version": "0.2.104", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.104.tgz", + "integrity": "sha512-1tqKRANSPTcjs/yjPoKh52oRM2u5AYdd8jie8sDiN8/5kpWWiQSHUGgtB4VEXLw1chVL3QPSPp8q9RWqzSn2FA==", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" diff --git a/package.json b/package.json index fd761b8f8..22607b6ab 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.103", + "lib0": "^0.2.104", "y-protocols": "^1.0.5" }, "devDependencies": { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index b06023e8a..f9ed7a775 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -102,7 +102,7 @@ class AttrRanges { * Split them if necessary. After split, i must insert the retainer at a valid position. * - merge items if neighbor has same attributes */ - for (let i = 0; i < ids.length - 1; i++) { + for (let i = 0; i < ids.length - 1;) { const range = ids[i] const nextRange = ids[i+1] // find out how to split range. it must match with next range. @@ -116,6 +116,7 @@ class AttrRanges { ids[i] = new AttrRange(range.clock, diff, range.attrs) ids.splice(i + 1, 0, new AttrRange(nextRange.clock, range.len - diff, range.attrs)) } + i++ continue } // now we know that range.clock === nextRange.clock @@ -125,13 +126,15 @@ class AttrRanges { ids[i] = new AttrRange(range.clock, smallerLen, amAttrRangeJoin(range.attrs, nextRange.attrs)) if (range.len === nextRange.len) { ids.splice(i + 1, 1) - i-- } else { ids[i + 1] = new AttrRange(range.clock + smallerLen, largerRange.len - smallerLen, largerRange.attrs) array.bubblesortItem(ids, i + 1, (a, b) => a.clock - b.clock) } + if (smallerLen === 0) i++ + } + while (ids.length > 0 && ids[0].len === 0) { + ids.splice(0, 1) } - // merge items without filtering or splicing the array. // i is the current pointer // j refers to the current insert position for the pointed item @@ -149,7 +152,7 @@ class AttrRanges { j++ } } - ids.length = ids[j - 1].len === 0 ? j - 1 : j + ids.length = ids.length === 0 ? 0 : (ids[j - 1].len === 0 ? j - 1 : j) } return ids } @@ -219,7 +222,7 @@ export class AttributionManager { let index = findIndexInIdRanges(ranges, id.clock) if (index !== null) { const res = [] - while (true) { + while (index < ranges.length) { let r = ranges[index] if (r.clock < id.clock) { r = new AttrRange(id.clock, r.len - (id.clock - r.clock), r.attrs) diff --git a/tests/AttributionManager.tests.js b/tests/AttributionManager.tests.js index 1a9cce9c9..a20ffd141 100644 --- a/tests/AttributionManager.tests.js +++ b/tests/AttributionManager.tests.js @@ -33,8 +33,12 @@ const createRandomAttributionManager = (gen, clients, clockRange, attrChoices) = const clockStart = prng.uint32(gen, 0, clockRange) const len = prng.uint32(gen, 0, clockRange - clockStart) const attrs = [prng.oneOf(gen, attrChoices)] + // maybe add another attr if (prng.bool(gen)) { - attrs.push(prng.oneOf(gen, attrChoices)) + const a = prng.oneOf(gen, attrChoices) + if (attrs.find((attr => attr === a)) == null) { + attrs.push(a) + } } attrMngr.add(client, clockStart, len, attrs) } @@ -91,7 +95,7 @@ export const testAmMerge = _tc => { t.group('no merge of overlapping id ranges with different attributes', () => { compareAttributionManagers( simpleConstructAttrs([[0, 1, 2, [1]], [0, 0, 2, [2]]]), - simpleConstructAttrs([[0, 0, 1, [2]], [0, 1, 1, [1, 2]], [0, 2, 1, [2]]]) + simpleConstructAttrs([[0, 0, 1, [2]], [0, 1, 1, [1, 2]], [0, 2, 1, [1]]]) ) }) } @@ -101,7 +105,7 @@ export const testAmMerge = _tc => { */ export const testRepeatMergingMultipleAttrManagers = tc => { const clients = 4 - const clockRange = 100 + const clockRange = 5 /** * @type {Array>} */ diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index 4601ad1fb..6db450d1d 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -198,7 +198,9 @@ export const testRepeatMergingMultipleIdsets = tc => { const mergedHas = merged.has(new ID(iclient, iclock)) const oneHas = idss.some(ids => ids.has(new ID(iclient, iclock))) t.assert(mergedHas === oneHas) - d.addToIdSet(composed, iclient, iclock, 1) + if (oneHas) { + d.addToIdSet(composed, iclient, iclock, 1) + } } } compareIdSets(merged, composed) From a36075161a8c2265b69942f0f5845b6a364dc0f0 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 12 Apr 2025 17:20:21 +0200 Subject: [PATCH 269/362] diffing of attribution manager state --- rollup.config.js | 15 ------- src/utils/AttributionManager.js | 26 ++++++++++-- src/utils/IdSet.js | 66 +++++++++++++++++++++--------- tests/AttributionManager.tests.js | 65 +++++++++++++++--------------- tests/IdSet.tests.js | 67 ++++++++++++++----------------- tests/testHelper.js | 53 +++++++++++++++++++++++- 6 files changed, 184 insertions(+), 108 deletions(-) diff --git a/rollup.config.js b/rollup.config.js index e54694fc5..7939c8246 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,12 +1,3 @@ -const resolver = { - resolveId (importee) { - return - if (importee === 'yjs') { - return `${process.cwd()}/src/index.js` - } - } -} - export default [{ // cjs output input: { @@ -20,9 +11,6 @@ export default [{ entryFileNames: '[name].cjs', sourcemap: true }, - plugins: [ - resolver - ], external: id => /^(lib0|y-protocols)\//.test(id) }, { // esm output @@ -37,8 +25,5 @@ export default [{ entryFileNames: '[name].mjs', sourcemap: true }, - plugins: [ - resolver - ], external: id => /^(lib0|y-protocols)\//.test(id) }] diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index f9ed7a775..f8696407b 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -1,6 +1,7 @@ import { + _diffSet, findIndexInIdRanges, - ID // @eslint-disable-line + IdSet, ID // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' @@ -50,12 +51,20 @@ export class AttrRange { */ this.attrs = attrs } + + /** + * @param {number} clock + * @param {number} len + */ + copyWith (clock, len) { + return new AttrRange(clock, len, this.attrs) + } } /** * @template Attrs */ -class AttrRanges { +export class AttrRanges { /** * @param {Array>} ids */ @@ -104,7 +113,7 @@ class AttrRanges { */ for (let i = 0; i < ids.length - 1;) { const range = ids[i] - const nextRange = ids[i+1] + const nextRange = ids[i + 1] // find out how to split range. it must match with next range. // 1) we have space. Split if necessary. // 2) concat attributes in range to the next range. Split range and splice the remainder at @@ -257,3 +266,14 @@ export class AttributionManager { } export const createAttributionManager = () => new AttributionManager() + +/** + * Remove all ranges from `exclude` from `ds`. The result is a fresh AttributionManager containing all ranges from `idSet` that are not + * in `exclude`. + * + * @template {AttributionManager} Set + * @param {Set} set + * @param {IdSet | AttributionManager} exclude + * @return {Set} + */ +export const diffAttributionManager = _diffSet diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 606386707..b3aa5df45 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -4,6 +4,8 @@ import { splitItem, iterateStructs, UpdateEncoderV2, + AttributionManager, + AttrRanges, AbstractStruct, DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line } from '../internals.js' @@ -27,6 +29,14 @@ export class IdRange { */ this.len = len } + + /** + * @param {number} clock + * @param {number} len + */ + copyWith (clock, len) { + return new IdRange(clock, len) + } } class IdRanges { @@ -203,54 +213,58 @@ export const insertIntoIdSet = (dest, src) => { }) } - /** * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not * in `exclude`. * - * @param {IdSet} idSet - * @param {IdSet} exclude - * @return {IdSet} + * @template {IdSet | AttributionManager} Set + * @param {Set} set + * @param {IdSet | AttributionManager} exclude + * @return {Set} */ -export const diffIdSets = (idSet, exclude) => { - const res = new IdSet() - idSet.clients.forEach((_idRanges, client) => { +export const _diffSet = (set, exclude) => { + /** + * @type {Set} + */ + const res = /** @type {any } */ (set instanceof IdSet ? new IdSet() : new AttributionManager()) + const Ranges = set instanceof IdSet ? IdRanges : AttrRanges + set.clients.forEach((_setRanges, client) => { /** * @type {Array} */ let resRanges = [] const _excludedRanges = exclude.clients.get(client) - const idRanges = _idRanges.getIds() + const setRanges = _setRanges.getIds() if (_excludedRanges == null) { - resRanges = idRanges.slice() + resRanges = setRanges.slice() } else { const excludedRanges = _excludedRanges.getIds() let i = 0; let j = 0 - let currRange = idRanges[0] - while (i < idRanges.length && j < excludedRanges.length) { + let currRange = setRanges[0] + while (i < setRanges.length && j < excludedRanges.length) { const e = excludedRanges[j] if (currRange.clock + currRange.len <= e.clock) { // no overlapping, use next range item if (currRange.len > 0) resRanges.push(currRange) - currRange = idRanges[++i] + currRange = setRanges[++i] } else if (e.clock + e.len <= currRange.clock) { // no overlapping, use next excluded item j++ } else if (e.clock <= currRange.clock) { // exclude laps into range (we already know that the ranges somehow collide) const newClock = e.clock + e.len const newLen = currRange.clock + currRange.len - newClock if (newLen > 0) { - currRange = new IdRange(newClock, newLen) + currRange = currRange.copyWith(newClock, newLen) j++ } else { // this item is completely overwritten. len=0. We can jump to the next range - currRange = idRanges[++i] + currRange = setRanges[++i] } } else { // currRange.clock < e.clock -- range laps into exclude => adjust len // beginning can't be empty, add it to the result const nextLen = e.clock - currRange.clock - resRanges.push(new IdRange(currRange.clock, nextLen)) + resRanges.push(currRange.copyWith(currRange.clock, nextLen)) // retain the remaining length after exclude in currRange - currRange = new IdRange(currRange.clock + e.len + nextLen, math.max(currRange.len - e.len - nextLen, 0)) - if (currRange.len === 0) currRange = idRanges[++i] + currRange = currRange.copyWith(currRange.clock + e.len + nextLen, math.max(currRange.len - e.len - nextLen, 0)) + if (currRange.len === 0) currRange = setRanges[++i] else j++ } } @@ -258,15 +272,27 @@ export const diffIdSets = (idSet, exclude) => { resRanges.push(currRange) } i++ - while (i < idRanges.length) { - resRanges.push(idRanges[i++]) + while (i < setRanges.length) { + resRanges.push(setRanges[i++]) } } - if (resRanges.length > 0) res.clients.set(client, new IdRanges(resRanges)) + // @ts-ignore + if (resRanges.length > 0) res.clients.set(client, /** @type {any} */ (new Ranges(resRanges))) }) return res } +/** + * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not + * in `exclude`. + * + * @template {IdSet} Set + * @param {Set} set + * @param {IdSet | AttributionManager} exclude + * @return {Set} + */ +export const diffIdSet = _diffSet + /** * @param {IdSet} idSet * @param {number} client diff --git a/tests/AttributionManager.tests.js b/tests/AttributionManager.tests.js index a20ffd141..0ce8e8353 100644 --- a/tests/AttributionManager.tests.js +++ b/tests/AttributionManager.tests.js @@ -1,8 +1,6 @@ import * as t from 'lib0/testing' import * as am from '../src/utils/AttributionManager.js' -import * as prng from 'lib0/prng' -import * as math from 'lib0/math' -import { compareAttributionManagers, createAttributionManager, ID } from './testHelper.js' +import { compareAttributionManagers, createAttributionManager, ID, createRandomIdSet, createRandomAttributionManager } from './testHelper.js' /** * @template T @@ -16,35 +14,6 @@ const simpleConstructAttrs = ops => { return attrs } -/** - * @template T - * @param {prng.PRNG} gen - * @param {number} clients - * @param {number} clockRange (max clock - exclusive - by each client) - * @param {Array} attrChoices (max clock - exclusive - by each client) - * @return {am.AttributionManager} - */ -const createRandomAttributionManager = (gen, clients, clockRange, attrChoices) => { - const maxOpLen = 5 - const numOfOps = math.ceil((clients * clockRange) / maxOpLen) - const attrMngr = createAttributionManager() - for (let i = 0; i < numOfOps; i++) { - const client = prng.uint32(gen, 0, clients - 1) - const clockStart = prng.uint32(gen, 0, clockRange) - const len = prng.uint32(gen, 0, clockRange - clockStart) - const attrs = [prng.oneOf(gen, attrChoices)] - // maybe add another attr - if (prng.bool(gen)) { - const a = prng.oneOf(gen, attrChoices) - if (attrs.find((attr => attr === a)) == null) { - attrs.push(a) - } - } - attrMngr.add(client, clockStart, len, attrs) - } - return attrMngr -} - /** * @param {t.TestCase} _tc */ @@ -133,3 +102,35 @@ export const testRepeatMergingMultipleAttrManagers = tc => { compareAttributionManagers(merged, composed) } +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDiffing = tc => { + const clients = 4 + const clockRange = 100 + const attrs = [1, 2, 3] + const ds1 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) + const ds2 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) + const merged = am.mergeAttributionManagers([ds1, ds2]) + const e1 = am.diffAttributionManager(ds1, ds2) + const e2 = am.diffAttributionManager(merged, ds2) + compareAttributionManagers(e1, e2) +} + +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDiffing2 = tc => { + const clients = 4 + const clockRange = 100 + const attrs = [1, 2, 3] + const am1 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) + const am2 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) + const idsExclude = createRandomIdSet(tc.prng, clients, clockRange) + const merged = am.mergeAttributionManagers([am1, am2]) + const mergedExcluded = am.diffAttributionManager(merged, idsExclude) + const e1 = am.diffAttributionManager(am1, idsExclude) + const e2 = am.diffAttributionManager(am2, idsExclude) + const excludedMerged = am.mergeAttributionManagers([e1, e2]) + compareAttributionManagers(mergedExcluded, excludedMerged) +} diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index 6db450d1d..afe990155 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -1,8 +1,6 @@ import * as t from 'lib0/testing' import * as d from '../src/utils/IdSet.js' -import * as prng from 'lib0/prng' -import * as math from 'lib0/math' -import { compareIdSets, ID } from './testHelper.js' +import { compareIdSets, createRandomIdSet, ID } from './testHelper.js' /** * @param {Array<[number, number, number]>} ops @@ -69,7 +67,7 @@ export const testIdsetMerge = _tc => { export const testDiffing = _tc => { t.group('simple case (1))', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 1], [0, 3, 1]]), simpleConstructIdSet([[0, 3, 1]]) ), @@ -78,7 +76,7 @@ export const testDiffing = _tc => { }) t.group('subset left', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3]]), simpleConstructIdSet([[0, 1, 1]]) ), @@ -87,7 +85,7 @@ export const testDiffing = _tc => { }) t.group('subset right', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3]]), simpleConstructIdSet([[0, 3, 1]]) ), @@ -96,7 +94,7 @@ export const testDiffing = _tc => { }) t.group('subset middle', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3]]), simpleConstructIdSet([[0, 2, 1]]) ), @@ -105,7 +103,7 @@ export const testDiffing = _tc => { }) t.group('overlapping left', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3]]), simpleConstructIdSet([[0, 0, 2]]) ), @@ -114,7 +112,7 @@ export const testDiffing = _tc => { }) t.group('overlapping right', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3]]), simpleConstructIdSet([[0, 3, 5]]) ), @@ -123,7 +121,7 @@ export const testDiffing = _tc => { }) t.group('overlapping completely', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3]]), simpleConstructIdSet([[0, 0, 5]]) ), @@ -132,7 +130,7 @@ export const testDiffing = _tc => { }) t.group('overlapping into new range', () => { compareIdSets( - d.diffIdSets( + d.diffIdSet( simpleConstructIdSet([[0, 1, 3], [0, 5, 2]]), simpleConstructIdSet([[0, 0, 6]]) ), @@ -141,38 +139,17 @@ export const testDiffing = _tc => { }) } -/** - * @param {prng.PRNG} gen - * @param {number} clients - * @param {number} clockRange (max clock - exclusive - by each client) - */ -const createRandomDiffSet = (gen, clients, clockRange) => { - const maxOpLen = 5 - const numOfOps = math.ceil((clients * clockRange) / maxOpLen) - const ds = d.createIdSet() - for (let i = 0; i < numOfOps; i++) { - const client = prng.uint32(gen, 0, clients - 1) - const clockStart = prng.uint32(gen, 0, clockRange) - const len = prng.uint32(gen, 0, clockRange - clockStart) - d.addToIdSet(ds, client, clockStart, len) - } - if (ds.clients.size === clients && clients > 1 && prng.bool(gen)) { - ds.clients.delete(prng.uint32(gen, 0, clients)) - } - return ds -} - /** * @param {t.TestCase} tc */ export const testRepeatRandomDiffing = tc => { const clients = 4 const clockRange = 100 - const ds1 = createRandomDiffSet(tc.prng, clients, clockRange) - const ds2 = createRandomDiffSet(tc.prng, clients, clockRange) + const ds1 = createRandomIdSet(tc.prng, clients, clockRange) + const ds2 = createRandomIdSet(tc.prng, clients, clockRange) const merged = d.mergeIdSets([ds1, ds2]) - const e1 = d.diffIdSets(ds1, ds2) - const e2 = d.diffIdSets(merged, ds2) + const e1 = d.diffIdSet(ds1, ds2) + const e2 = d.diffIdSet(merged, ds2) compareIdSets(e1, e2) } @@ -187,7 +164,7 @@ export const testRepeatMergingMultipleIdsets = tc => { */ const idss = [] for (let i = 0; i < 3; i++) { - idss.push(createRandomDiffSet(tc.prng, clients, clockRange)) + idss.push(createRandomIdSet(tc.prng, clients, clockRange)) } const merged = d.mergeIdSets(idss) const mergedReverse = d.mergeIdSets(idss.reverse()) @@ -206,3 +183,19 @@ export const testRepeatMergingMultipleIdsets = tc => { compareIdSets(merged, composed) } +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDiffing2 = tc => { + const clients = 4 + const clockRange = 100 + const ids1 = createRandomIdSet(tc.prng, clients, clockRange) + const ids2 = createRandomIdSet(tc.prng, clients, clockRange) + const idsExclude = createRandomIdSet(tc.prng, clients, clockRange) + const merged = d.mergeIdSets([ids1, ids2]) + const mergedExcluded = d.diffIdSet(merged, idsExclude) + const e1 = d.diffIdSet(ids1, idsExclude) + const e2 = d.diffIdSet(ids2, idsExclude) + const excludedMerged = d.mergeIdSets([e1, e2]) + compareIdSets(mergedExcluded, excludedMerged) +} diff --git a/tests/testHelper.js b/tests/testHelper.js index acf38b932..d7d896482 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -6,7 +6,10 @@ import * as syncProtocol from 'y-protocols/sync' import * as object from 'lib0/object' import * as map from 'lib0/map' import * as Y from '../src/index.js' -import { amAttrsEqual } from '../src/internals.js' +import * as math from 'lib0/math' +import { + amAttrsEqual, createIdSet, createAttributionManager, addToIdSet +} from '../src/internals.js' export * from '../src/index.js' @@ -344,7 +347,55 @@ export const compareAttributionManagers = (am1, am2) => { return true } +/** + * @param {prng.PRNG} gen + * @param {number} clients + * @param {number} clockRange (max clock - exclusive - by each client) + */ +export const createRandomIdSet = (gen, clients, clockRange) => { + const maxOpLen = 5 + const numOfOps = math.ceil((clients * clockRange) / maxOpLen) + const ds = createIdSet() + for (let i = 0; i < numOfOps; i++) { + const client = prng.uint32(gen, 0, clients - 1) + const clockStart = prng.uint32(gen, 0, clockRange) + const len = prng.uint32(gen, 0, clockRange - clockStart) + addToIdSet(ds, client, clockStart, len) + } + if (ds.clients.size === clients && clients > 1 && prng.bool(gen)) { + ds.clients.delete(prng.uint32(gen, 0, clients)) + } + return ds +} +/** + * @template T + * @param {prng.PRNG} gen + * @param {number} clients + * @param {number} clockRange (max clock - exclusive - by each client) + * @param {Array} attrChoices (max clock - exclusive - by each client) + * @return {Y.AttributionManager} + */ +export const createRandomAttributionManager = (gen, clients, clockRange, attrChoices) => { + const maxOpLen = 5 + const numOfOps = math.ceil((clients * clockRange) / maxOpLen) + const attrMngr = createAttributionManager() + for (let i = 0; i < numOfOps; i++) { + const client = prng.uint32(gen, 0, clients - 1) + const clockStart = prng.uint32(gen, 0, clockRange) + const len = prng.uint32(gen, 0, clockRange - clockStart) + const attrs = [prng.oneOf(gen, attrChoices)] + // maybe add another attr + if (prng.bool(gen)) { + const a = prng.oneOf(gen, attrChoices) + if (attrs.find(attr => attr === a) == null) { + attrs.push(a) + } + } + attrMngr.add(client, clockStart, len, attrs) + } + return attrMngr +} /** * 1. reconnect and flush all From 55238e0faf85f0f927ef9297040b35c163430b1f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 12 Apr 2025 17:30:06 +0200 Subject: [PATCH 270/362] ds is no longer maintained on store - improves perf --- src/utils/StructStore.js | 9 ++++++--- src/utils/Transaction.js | 2 +- src/utils/encoding.js | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index fa4cd23c1..8aa0000a8 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -1,8 +1,8 @@ import { GC, splitItem, - IdSet, - Transaction, ID, Item // eslint-disable-line + Transaction, ID, Item, // eslint-disable-line + createDeleteSetFromStructStore } from '../internals.js' import * as math from 'lib0/math' @@ -14,7 +14,7 @@ export class StructStore { * @type {Map>} */ this.clients = new Map() - this.ds = new IdSet() + // this.ds = new IdSet() /** * @type {null | { missing: Map, update: Uint8Array }} */ @@ -24,6 +24,9 @@ export class StructStore { */ this.pendingDs = null } + get ds () { + return createDeleteSetFromStructStore(this) + } } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index feebe5baa..255b33256 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -308,7 +308,7 @@ const cleanupTransactions = (transactionCleanups, i) => { const store = doc.store const ds = transaction.deleteSet const mergeStructs = transaction._mergeStructs - insertIntoIdSet(store.ds, ds) + // insertIntoIdSet(store.ds, ds) try { doc.emit('beforeObserverCalls', [transaction, doc]) /** diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 097d96e8e..f2a5a8f4e 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -35,7 +35,7 @@ import { Skip, diffUpdateV2, convertUpdateFormatV2ToV1, - IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line + IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, createDeleteSetFromStructStore, // eslint-disable-line } from '../internals.js' import * as encoding from 'lib0/encoding' From 1d025ae73fe438055d63336f2d88cd6862d1bc94 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 18 Apr 2025 20:26:05 +0200 Subject: [PATCH 271/362] rename AttributionManager=>IdMap The "AttributionManager" will be an abstract class that maps data (probably using IdMap(s)) --- src/index.js | 4 +- src/internals.js | 2 +- src/types/YText.js | 6 +- src/utils/{AttributionManager.js => IdMap.js} | 30 ++++----- src/utils/IdSet.js | 10 +-- ...ibutionManager.tests.js => IdMap.tests.js} | 66 +++++++++---------- tests/index.js | 4 +- tests/testHelper.js | 22 +++---- 8 files changed, 72 insertions(+), 72 deletions(-) rename src/utils/{AttributionManager.js => IdMap.js} (88%) rename tests/{AttributionManager.tests.js => IdMap.tests.js} (60%) diff --git a/src/index.js b/src/index.js index 5adfac567..5f9433c4a 100644 --- a/src/index.js +++ b/src/index.js @@ -102,8 +102,8 @@ export { IdSet, equalIdSets, createDeleteSetFromStructStore, - AttributionManager, - createAttributionManager + IdMap, + createIdMap } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/internals.js b/src/internals.js index 6f9e5de24..f7005afd2 100644 --- a/src/internals.js +++ b/src/internals.js @@ -40,4 +40,4 @@ export * from './structs/ContentString.js' export * from './structs/ContentType.js' export * from './structs/Item.js' export * from './structs/Skip.js' -export * from './utils/AttributionManager.js' +export * from './utils/IdMap.js' diff --git a/src/types/YText.js b/src/types/YText.js index 3e4b4986c..0a70f371d 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -26,7 +26,7 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, AttributionManager, // eslint-disable-line + ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, IdMap, // eslint-disable-line snapshot } from '../internals.js' @@ -999,13 +999,13 @@ export class YText extends AbstractType { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @param {AttributionManager} [attributionManager] + * @param {IdMap} [idMap] * @param {Doc} [prevYdoc] * @return {import('../utils/Delta.js').Delta} The Delta representation of this type. * * @public */ - getContent (attributionManager, prevYdoc) { + getContent (idMap, prevYdoc) { this.doc ?? warnPrematureAccess() const prevSnapshot = prevYdoc ? snapshot(prevYdoc) : null const d = delta.create() diff --git a/src/utils/AttributionManager.js b/src/utils/IdMap.js similarity index 88% rename from src/utils/AttributionManager.js rename to src/utils/IdMap.js index f8696407b..03add22cc 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/IdMap.js @@ -12,21 +12,21 @@ import * as array from 'lib0/array' * @param {T} attr * */ -const amAttrsHas = (attrs, attr) => attrs.find(a => a === attr) +const idmapAttrsHas = (attrs, attr) => attrs.find(a => a === attr) /** * @template T * @param {Array} a * @param {Array} b */ -export const amAttrsEqual = (a, b) => a.length === b.length && a.every(v => amAttrsHas(b, v)) +export const idmapAttrsEqual = (a, b) => a.length === b.length && a.every(v => idmapAttrsHas(b, v)) /** * @template T * @param {Array} a * @param {Array} b */ -const amAttrRangeJoin = (a, b) => a.concat(b.filter(attr => !amAttrsHas(a, attr))) +const idmapAttrRangeJoin = (a, b) => a.concat(b.filter(attr => !idmapAttrsHas(a, attr))) /** * @template Attrs @@ -132,7 +132,7 @@ export class AttrRanges { // merge range with nextRange const largerRange = range.len > nextRange.len ? range : nextRange const smallerLen = range.len < nextRange.len ? range.len : nextRange.len - ids[i] = new AttrRange(range.clock, smallerLen, amAttrRangeJoin(range.attrs, nextRange.attrs)) + ids[i] = new AttrRange(range.clock, smallerLen, idmapAttrRangeJoin(range.attrs, nextRange.attrs)) if (range.len === nextRange.len) { ids.splice(i + 1, 1) } else { @@ -152,7 +152,7 @@ export class AttrRanges { for (i = 1, j = 1; i < ids.length; i++) { const left = ids[j - 1] const right = ids[i] - if (left.clock + left.len === right.clock && amAttrsEqual(left.attrs, right.attrs)) { + if (left.clock + left.len === right.clock && idmapAttrsEqual(left.attrs, right.attrs)) { ids[j - 1] = new AttrRange(left.clock, left.len + right.len, left.attrs) } else if (right.len !== 0) { if (j < i) { @@ -169,11 +169,11 @@ export class AttrRanges { /** * @template T - * @param {Array>} ams - * @return {AttributionManager} A fresh IdSet + * @param {Array>} ams + * @return {IdMap} A fresh IdSet */ -export const mergeAttributionManagers = ams => { - const merged = createAttributionManager() +export const mergeIdMaps = ams => { + const merged = createIdMap() for (let amsI = 0; amsI < ams.length; amsI++) { ams[amsI].clients.forEach((rangesLeft, client) => { if (!merged.clients.has(client)) { @@ -196,7 +196,7 @@ export const mergeAttributionManagers = ams => { /** * @template Attrs */ -export class AttributionManager { +export class IdMap { constructor () { /** * @type {Map>} @@ -265,15 +265,15 @@ export class AttributionManager { } } -export const createAttributionManager = () => new AttributionManager() +export const createIdMap = () => new IdMap() /** - * Remove all ranges from `exclude` from `ds`. The result is a fresh AttributionManager containing all ranges from `idSet` that are not + * Remove all ranges from `exclude` from `ds`. The result is a fresh IdMap containing all ranges from `idSet` that are not * in `exclude`. * - * @template {AttributionManager} Set + * @template {IdMap} Set * @param {Set} set - * @param {IdSet | AttributionManager} exclude + * @param {IdSet | IdMap} exclude * @return {Set} */ -export const diffAttributionManager = _diffSet +export const diffIdMap = _diffSet diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index b3aa5df45..21efe141f 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -4,7 +4,7 @@ import { splitItem, iterateStructs, UpdateEncoderV2, - AttributionManager, + IdMap, AttrRanges, AbstractStruct, DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line } from '../internals.js' @@ -217,16 +217,16 @@ export const insertIntoIdSet = (dest, src) => { * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not * in `exclude`. * - * @template {IdSet | AttributionManager} Set + * @template {IdSet | IdMap} Set * @param {Set} set - * @param {IdSet | AttributionManager} exclude + * @param {IdSet | IdMap} exclude * @return {Set} */ export const _diffSet = (set, exclude) => { /** * @type {Set} */ - const res = /** @type {any } */ (set instanceof IdSet ? new IdSet() : new AttributionManager()) + const res = /** @type {any } */ (set instanceof IdSet ? new IdSet() : new IdMap()) const Ranges = set instanceof IdSet ? IdRanges : AttrRanges set.clients.forEach((_setRanges, client) => { /** @@ -288,7 +288,7 @@ export const _diffSet = (set, exclude) => { * * @template {IdSet} Set * @param {Set} set - * @param {IdSet | AttributionManager} exclude + * @param {IdSet | IdMap} exclude * @return {Set} */ export const diffIdSet = _diffSet diff --git a/tests/AttributionManager.tests.js b/tests/IdMap.tests.js similarity index 60% rename from tests/AttributionManager.tests.js rename to tests/IdMap.tests.js index 0ce8e8353..a66922577 100644 --- a/tests/AttributionManager.tests.js +++ b/tests/IdMap.tests.js @@ -1,13 +1,13 @@ import * as t from 'lib0/testing' -import * as am from '../src/utils/AttributionManager.js' -import { compareAttributionManagers, createAttributionManager, ID, createRandomIdSet, createRandomAttributionManager } from './testHelper.js' +import * as am from '../src/utils/IdMap.js' +import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap } from './testHelper.js' /** * @template T * @param {Array<[number, number, number, Array]>} ops */ const simpleConstructAttrs = ops => { - const attrs = createAttributionManager() + const attrs = createIdMap() ops.forEach(op => { attrs.add(op[0], op[1], op[2], op[3]) }) @@ -20,49 +20,49 @@ const simpleConstructAttrs = ops => { export const testAmMerge = _tc => { const attrs = [42] t.group('filter out empty items (1))', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 0, attrs]]), simpleConstructAttrs([]) ) }) t.group('filter out empty items (2))', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 0, attrs], [0, 2, 0, attrs]]), simpleConstructAttrs([]) ) }) t.group('filter out empty items (3 - end))', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 1, attrs], [0, 2, 0, attrs]]), simpleConstructAttrs([[0, 1, 1, attrs]]) ) }) t.group('filter out empty items (4 - middle))', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 1, attrs], [0, 2, 0, attrs], [0, 3, 1, attrs]]), simpleConstructAttrs([[0, 1, 1, attrs], [0, 3, 1, attrs]]) ) }) t.group('filter out empty items (5 - beginning))', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 0, attrs], [0, 2, 1, attrs], [0, 3, 1, attrs]]), simpleConstructAttrs([[0, 2, 1, attrs], [0, 3, 1, attrs]]) ) }) t.group('merge of overlapping id ranges', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 2, attrs], [0, 0, 2, attrs]]), simpleConstructAttrs([[0, 0, 3, attrs]]) ) }) t.group('construct without hole', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 2, attrs], [0, 3, 1, attrs]]), simpleConstructAttrs([[0, 1, 3, attrs]]) ) }) t.group('no merge of overlapping id ranges with different attributes', () => { - compareAttributionManagers( + compareIdmaps( simpleConstructAttrs([[0, 1, 2, [1]], [0, 0, 2, [2]]]), simpleConstructAttrs([[0, 0, 1, [2]], [0, 1, 1, [1, 2]], [0, 2, 1, [1]]]) ) @@ -72,20 +72,20 @@ export const testAmMerge = _tc => { /** * @param {t.TestCase} tc */ -export const testRepeatMergingMultipleAttrManagers = tc => { +export const testRepeatMergingMultipleIdMaps = tc => { const clients = 4 const clockRange = 5 /** - * @type {Array>} + * @type {Array>} */ const sets = [] for (let i = 0; i < 3; i++) { - sets.push(createRandomAttributionManager(tc.prng, clients, clockRange, [1, 2, 3])) + sets.push(createRandomIdMap(tc.prng, clients, clockRange, [1, 2, 3])) } - const merged = am.mergeAttributionManagers(sets) - const mergedReverse = am.mergeAttributionManagers(sets.reverse()) - compareAttributionManagers(merged, mergedReverse) - const composed = am.createAttributionManager() + const merged = am.mergeIdMaps(sets) + const mergedReverse = am.mergeIdMaps(sets.reverse()) + compareIdmaps(merged, mergedReverse) + const composed = am.createIdMap() for (let iclient = 0; iclient < clients; iclient++) { for (let iclock = 0; iclock < clockRange + 42; iclock++) { const mergedHas = merged.has(new ID(iclient, iclock)) @@ -99,7 +99,7 @@ export const testRepeatMergingMultipleAttrManagers = tc => { } } } - compareAttributionManagers(merged, composed) + compareIdmaps(merged, composed) } /** @@ -109,12 +109,12 @@ export const testRepeatRandomDiffing = tc => { const clients = 4 const clockRange = 100 const attrs = [1, 2, 3] - const ds1 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) - const ds2 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) - const merged = am.mergeAttributionManagers([ds1, ds2]) - const e1 = am.diffAttributionManager(ds1, ds2) - const e2 = am.diffAttributionManager(merged, ds2) - compareAttributionManagers(e1, e2) + const ds1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const ds2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const merged = am.mergeIdMaps([ds1, ds2]) + const e1 = am.diffIdMap(ds1, ds2) + const e2 = am.diffIdMap(merged, ds2) + compareIdmaps(e1, e2) } /** @@ -124,13 +124,13 @@ export const testRepeatRandomDiffing2 = tc => { const clients = 4 const clockRange = 100 const attrs = [1, 2, 3] - const am1 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) - const am2 = createRandomAttributionManager(tc.prng, clients, clockRange, attrs) + const am1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const am2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) const idsExclude = createRandomIdSet(tc.prng, clients, clockRange) - const merged = am.mergeAttributionManagers([am1, am2]) - const mergedExcluded = am.diffAttributionManager(merged, idsExclude) - const e1 = am.diffAttributionManager(am1, idsExclude) - const e2 = am.diffAttributionManager(am2, idsExclude) - const excludedMerged = am.mergeAttributionManagers([e1, e2]) - compareAttributionManagers(mergedExcluded, excludedMerged) + const merged = am.mergeIdMaps([am1, am2]) + const mergedExcluded = am.diffIdMap(merged, idsExclude) + const e1 = am.diffIdMap(am1, idsExclude) + const e2 = am.diffIdMap(am2, idsExclude) + const excludedMerged = am.mergeIdMaps([e1, e2]) + compareIdmaps(mergedExcluded, excludedMerged) } diff --git a/tests/index.js b/tests/index.js index fd129a27c..753174da4 100644 --- a/tests/index.js +++ b/tests/index.js @@ -13,7 +13,7 @@ import * as updates from './updates.tests.js' import * as relativePositions from './relativePositions.tests.js' import * as delta from './delta.tests.js' import * as idset from './IdSet.tests.js' -import * as attributionManager from './AttributionManager.tests.js' +import * as idmap from './IdMap.tests.js' import { runTests } from 'lib0/testing' import { isBrowser, isNode } from 'lib0/environment' @@ -24,7 +24,7 @@ if (isBrowser) { } const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset, attributionManager + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset, idmap } const run = async () => { diff --git a/tests/testHelper.js b/tests/testHelper.js index d7d896482..8b1467974 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -8,7 +8,7 @@ import * as map from 'lib0/map' import * as Y from '../src/index.js' import * as math from 'lib0/math' import { - amAttrsEqual, createIdSet, createAttributionManager, addToIdSet + idmapAttrsEqual, createIdSet, createIdMap, addToIdSet } from '../src/internals.js' export * from '../src/index.js' @@ -329,10 +329,10 @@ export const compareIdSets = (idSet1, idSet2) => { /** * @template T - * @param {Y.AttributionManager} am1 - * @param {Y.AttributionManager} am2 + * @param {Y.IdMap} am1 + * @param {Y.IdMap} am2 */ -export const compareAttributionManagers = (am1, am2) => { +export const compareIdmaps = (am1, am2) => { if (am1.clients.size !== am2.clients.size) return false for (const [client, _items1] of am1.clients.entries()) { const items1 = _items1.getIds() @@ -340,8 +340,8 @@ export const compareAttributionManagers = (am1, am2) => { t.assert(items2 !== undefined && items1.length === items2.length) for (let i = 0; i < items1.length; i++) { const di1 = items1[i] - const di2 = /** @type {Array>} */ (items2)[i] - t.assert(di1.clock === di2.clock && di1.len === di2.len && amAttrsEqual(di1.attrs, di2.attrs)) + const di2 = /** @type {Array>} */ (items2)[i] + t.assert(di1.clock === di2.clock && di1.len === di2.len && idmapAttrsEqual(di1.attrs, di2.attrs)) } } return true @@ -374,12 +374,12 @@ export const createRandomIdSet = (gen, clients, clockRange) => { * @param {number} clients * @param {number} clockRange (max clock - exclusive - by each client) * @param {Array} attrChoices (max clock - exclusive - by each client) - * @return {Y.AttributionManager} + * @return {Y.IdMap} */ -export const createRandomAttributionManager = (gen, clients, clockRange, attrChoices) => { +export const createRandomIdMap = (gen, clients, clockRange, attrChoices) => { const maxOpLen = 5 const numOfOps = math.ceil((clients * clockRange) / maxOpLen) - const attrMngr = createAttributionManager() + const idMap = createIdMap() for (let i = 0; i < numOfOps; i++) { const client = prng.uint32(gen, 0, clients - 1) const clockStart = prng.uint32(gen, 0, clockRange) @@ -392,9 +392,9 @@ export const createRandomAttributionManager = (gen, clients, clockRange, attrCho attrs.push(a) } } - attrMngr.add(client, clockStart, len, attrs) + idMap.add(client, clockStart, len, attrs) } - return attrMngr + return idMap } /** From 2d87301af2fb8fb05e339b4da5dd60fa60f43959 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Apr 2025 00:21:40 +0200 Subject: [PATCH 272/362] implement attribution class that is de-duplicated in IdMap --- src/index.js | 4 +- src/utils/IdMap.js | 94 +++++++++++++++++++++++++++++++++++++--- src/utils/StructStore.js | 1 + src/utils/Transaction.js | 2 +- tests/IdMap.tests.js | 4 +- tests/testHelper.js | 28 ++++++++++-- 6 files changed, 120 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index 5f9433c4a..325092a2a 100644 --- a/src/index.js +++ b/src/index.js @@ -103,7 +103,9 @@ export { equalIdSets, createDeleteSetFromStructStore, IdMap, - createIdMap + createIdMap, + createAttribution, + Attribution } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 03add22cc..d91f9e024 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -5,6 +5,50 @@ import { } from '../internals.js' import * as array from 'lib0/array' +import * as map from 'lib0/map' +import * as encoding from 'lib0/encoding' +import * as buf from 'lib0/buffer' +import * as rabin from 'lib0/hash/rabin' + +/** + * @template V + */ +export class Attribution { + /** + * @param {string} name + * @param {V} val + */ + constructor (name, val) { + this.name = name + this.val = val + } + + hash () { + const encoder = encoding.createEncoder() + encoding.writeVarString(encoder, this.name) + encoding.writeAny(encoder, /** @type {any} */ (this.val)) + return buf.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder))) + } +} + +/** + * @param {Attribution} attr + */ +const _hashAttribution = attr => { + const encoder = encoding.createEncoder() + encoding.writeVarString(encoder, attr.name) + encoding.writeAny(encoder, attr.val) + return buf.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder))) +} + + +/** + * @template V + * @param {string} name + * @param {V} val + * @return {Attribution} + */ +export const createAttribution = (name, val) => new Attribution(name, val) /** * @template T @@ -35,7 +79,7 @@ export class AttrRange { /** * @param {number} clock * @param {number} len - * @param {Array} attrs + * @param {Array>} attrs */ constructor (clock, len, attrs) { /** @@ -79,7 +123,7 @@ export class AttrRanges { /** * @param {number} clock * @param {number} length - * @param {Array} attrs + * @param {Array>} attrs */ add (clock, length, attrs) { this.sorted = false @@ -168,11 +212,20 @@ export class AttrRanges { } /** + * Merge multiple idmaps. Ensures that there are no redundant attribution definitions (two + * Attributions that describe the same thing). + * * @template T * @param {Array>} ams * @return {IdMap} A fresh IdSet */ export const mergeIdMaps = ams => { + /** + * Maps attribution to the attribution of the merged idmap. + * + * @type {Map,Attribution>} + */ + const attrMapper = new Map() const merged = createIdMap() for (let amsI = 0; amsI < ams.length; amsI++) { ams[amsI].clients.forEach((rangesLeft, client) => { @@ -186,6 +239,14 @@ export const mergeIdMaps = ams => { array.appendTo(ids, nextIds.getIds()) } } + ids.forEach(id => { + // @ts-ignore + id.attrs = id.attrs.map(attr => + map.setIfUndefined(attrMapper, attr, () => + _ensureAttrs(merged, [attr])[0] + ) + ) + }) merged.clients.set(client, new AttrRanges(ids)) } }) @@ -202,6 +263,14 @@ export class IdMap { * @type {Map>} */ this.clients = new Map() + /** + * @type {Map>} + */ + this.attrsH = new Map() + /** + * @type {Set>} + */ + this.attrs = new Set() } /** @@ -253,9 +322,10 @@ export class IdMap { * @param {number} client * @param {number} clock * @param {number} len - * @param {Array} attrs + * @param {Array>} attrs */ add (client, clock, len, attrs) { + attrs = _ensureAttrs(this, attrs) const ranges = this.clients.get(client) if (ranges == null) { this.clients.set(client, new AttrRanges([new AttrRange(clock, len, attrs)])) @@ -265,15 +335,27 @@ export class IdMap { } } +/** + * @template Attrs + * @param {IdMap} idmap + * @param {Array>} attrs + * @return {Array>} + */ +const _ensureAttrs = (idmap, attrs) => attrs.map(attr => + idmap.attrs.has(attr) ? attr : map.setIfUndefined(idmap.attrsH, _hashAttribution(attr), () => { + idmap.attrs.add(attr) + return attr + })) + export const createIdMap = () => new IdMap() /** * Remove all ranges from `exclude` from `ds`. The result is a fresh IdMap containing all ranges from `idSet` that are not * in `exclude`. * - * @template {IdMap} Set - * @param {Set} set + * @template {IdMap} ISet + * @param {ISet} set * @param {IdSet | IdMap} exclude - * @return {Set} + * @return {ISet} */ export const diffIdMap = _diffSet diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 8aa0000a8..72438dbb5 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -24,6 +24,7 @@ export class StructStore { */ this.pendingDs = null } + get ds () { return createDeleteSetFromStructStore(this) } diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 255b33256..c90f3b768 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -11,7 +11,7 @@ import { createID, cleanupYTextAfterTransaction, IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, // eslint-disable-line - insertIntoIdSet + // insertIntoIdSet } from '../internals.js' import * as error from 'lib0/error' diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index a66922577..24388e32b 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,6 +1,6 @@ import * as t from 'lib0/testing' import * as am from '../src/utils/IdMap.js' -import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap } from './testHelper.js' +import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js' /** * @template T @@ -9,7 +9,7 @@ import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap } const simpleConstructAttrs = ops => { const attrs = createIdMap() ops.forEach(op => { - attrs.add(op[0], op[1], op[2], op[3]) + attrs.add(op[0], op[1], op[2], op[3].map(v => createAttribution('', v))) }) return attrs } diff --git a/tests/testHelper.js b/tests/testHelper.js index 8b1467974..6e55c746c 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -8,7 +8,7 @@ import * as map from 'lib0/map' import * as Y from '../src/index.js' import * as math from 'lib0/math' import { - idmapAttrsEqual, createIdSet, createIdMap, addToIdSet + createIdSet, createIdMap, addToIdSet } from '../src/internals.js' export * from '../src/index.js' @@ -327,6 +327,28 @@ export const compareIdSets = (idSet1, idSet2) => { return true } +/** + * only use for testing + * + * @template T + * @param {Array>} attrs + * @param {Y.Attribution} attr + * + */ +const _idmapAttrsHas = (attrs, attr) => { + const hash = attr.hash() + return attrs.find(a => a.hash() === hash) +} + +/** + * only use for testing + * + * @template T + * @param {Array>} a + * @param {Array>} b + */ +export const _idmapAttrsEqual = (a, b) => a.length === b.length && a.every(v => _idmapAttrsHas(b, v)) + /** * @template T * @param {Y.IdMap} am1 @@ -341,7 +363,7 @@ export const compareIdmaps = (am1, am2) => { for (let i = 0; i < items1.length; i++) { const di1 = items1[i] const di2 = /** @type {Array>} */ (items2)[i] - t.assert(di1.clock === di2.clock && di1.len === di2.len && idmapAttrsEqual(di1.attrs, di2.attrs)) + t.assert(di1.clock === di2.clock && di1.len === di2.len && _idmapAttrsEqual(di1.attrs, di2.attrs)) } } return true @@ -392,7 +414,7 @@ export const createRandomIdMap = (gen, clients, clockRange, attrChoices) => { attrs.push(a) } } - idMap.add(client, clockStart, len, attrs) + idMap.add(client, clockStart, len, attrs.map(v => Y.createAttribution('', v))) } return idMap } From 99bcafe1936858d0ef5452581d691466207fc082 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Apr 2025 15:15:34 +0200 Subject: [PATCH 273/362] efficient encoding & decoding of IdMaps --- src/utils/IdMap.js | 163 +++++++++++++++++++++++++++++++++++++++++-- src/utils/IdSet.js | 1 + tests/IdMap.tests.js | 5 ++ tests/testHelper.js | 31 ++++++-- 4 files changed, 189 insertions(+), 11 deletions(-) diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index d91f9e024..8459a6eef 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -1,12 +1,13 @@ import { _diffSet, findIndexInIdRanges, - IdSet, ID // eslint-disable-line + DSDecoderV1, DSDecoderV2, DSEncoderV1, DSEncoderV2, IdSet, ID // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' import * as map from 'lib0/map' import * as encoding from 'lib0/encoding' +import * as decoding from 'lib0/decoding' import * as buf from 'lib0/buffer' import * as rabin from 'lib0/hash/rabin' @@ -41,7 +42,6 @@ const _hashAttribution = attr => { return buf.toBase64(rabin.fingerprint(rabin.StandardIrreducible128, encoding.toUint8Array(encoder))) } - /** * @template V * @param {string} name @@ -126,6 +126,7 @@ export class AttrRanges { * @param {Array>} attrs */ add (clock, length, attrs) { + if (length === 0) return this.sorted = false this._ids.push(new AttrRange(clock, length, attrs)) } @@ -325,6 +326,7 @@ export class IdMap { * @param {Array>} attrs */ add (client, clock, len, attrs) { + if (len === 0) return attrs = _ensureAttrs(this, attrs) const ranges = this.clients.get(client) if (ranges == null) { @@ -335,6 +337,146 @@ export class IdMap { } } +/** + * Efficiently encodes IdMap to a binary form. Ensures that information is de-duplicated when + * written. Attribute.names are referenced by id. Attributes themselfs are also referenced by id. + * + * @template Attr + * @param {DSEncoderV1 | DSEncoderV2} encoder + * @param {IdMap} idmap + * + * @private + * @function + */ +export const writeIdMap = (encoder, idmap) => { + encoding.writeVarUint(encoder.restEncoder, idmap.clients.size) + let lastWrittenClientId = 0 + /** + * @type {Map, number>} + */ + const visitedAttributions = map.create() + /** + * @type {Map} + */ + const visitedAttrNames = map.create() + // Ensure that the delete set is written in a deterministic order (smaller clientids first) + array.from(idmap.clients.entries()) + .sort((a, b) => a[0] - b[0]) + .forEach(([client, _idRanges]) => { + const attrRanges = _idRanges.getIds() + encoder.resetDsCurVal() + const diff = client - lastWrittenClientId + encoding.writeVarUint(encoder.restEncoder, diff) + lastWrittenClientId = client + const len = attrRanges.length + encoding.writeVarUint(encoder.restEncoder, len) + for (let i = 0; i < len; i++) { + const item = attrRanges[i] + const attrs = item.attrs + const attrLen = attrs.length + encoder.writeDsClock(item.clock) + encoder.writeDsLen(item.len) + encoding.writeVarUint(encoder.restEncoder, attrLen) + for (let j = 0; j < attrLen; j++) { + const attr = attrs[j] + const attrId = visitedAttributions.get(attr) + if (attrId != null) { + encoding.writeVarUint(encoder.restEncoder, attrId) + } else { + const newAttrId = visitedAttributions.size + visitedAttributions.set(attr, newAttrId) + encoding.writeVarUint(encoder.restEncoder, newAttrId) + const attrNameId = visitedAttrNames.get(attr.name) + // write attr.name + if (attrNameId != null) { + encoding.writeVarUint(encoder.restEncoder, attrNameId) + } else { + const newAttrNameId = visitedAttrNames.size + encoding.writeVarUint(encoder.restEncoder, newAttrNameId) + encoding.writeVarString(encoder.restEncoder, attr.name) + visitedAttrNames.set(attr.name, newAttrNameId) + } + encoding.writeAny(encoder.restEncoder, /** @type {any} */ (attr.val)) + } + } + } + }) +} + +/** + * @param {IdMap} idmap + */ +export const encodeIdMap = idmap => { + const encoder = new DSEncoderV2() + writeIdMap(encoder, idmap) + return encoder.toUint8Array() +} + +/** + * @param {DSDecoderV1 | DSDecoderV2} decoder + * @return {IdMap} + * + * @private + * @function + */ +export const readIdMap = decoder => { + const idmap = new IdMap() + const numClients = decoding.readVarUint(decoder.restDecoder) + /** + * @type {Array>} + */ + const visitedAttributions = [] + /** + * @type {Array} + */ + const visitedAttrNames = [] + let lastClientId = 0 + for (let i = 0; i < numClients; i++) { + decoder.resetDsCurVal() + const client = lastClientId + decoding.readVarUint(decoder.restDecoder) + lastClientId = client + const numberOfDeletes = decoding.readVarUint(decoder.restDecoder) + /** + * @type {Array>} + */ + const attrRanges = [] + for (let i = 0; i < numberOfDeletes; i++) { + const rangeClock = decoder.readDsClock() + const rangeLen = decoder.readDsLen() + /** + * @type {Array>} + */ + const attrs = [] + const attrsLen = decoding.readVarUint(decoder.restDecoder) + for (let j = 0; j < attrsLen; j++) { + const attrId = decoding.readVarUint(decoder.restDecoder) + if (attrId >= visitedAttributions.length) { + // attrId not known yet + const attrNameId = decoding.readVarUint(decoder.restDecoder) + if (attrNameId >= visitedAttrNames.length) { + visitedAttrNames.push(decoding.readVarString(decoder.restDecoder)) + } + visitedAttributions.push(new Attribution(visitedAttrNames[attrNameId], decoding.readAny(decoder.restDecoder))) + } + attrs.push(visitedAttributions[attrId]) + } + attrRanges.push(new AttrRange(rangeClock, rangeLen, attrs)) + } + idmap.clients.set(client, new AttrRanges(attrRanges)) + } + visitedAttributions.forEach(attr => { + idmap.attrs.add(attr) + idmap.attrsH.set(attr.hash(), attr) + }) + return idmap +} + +/** + * @param {Uint8Array} data + * @return {IdMap} + */ +export const decodeIdMap = data => readIdMap(new DSDecoderV2(decoding.createDecoder(data))) + /** * @template Attrs * @param {IdMap} idmap @@ -342,10 +484,12 @@ export class IdMap { * @return {Array>} */ const _ensureAttrs = (idmap, attrs) => attrs.map(attr => - idmap.attrs.has(attr) ? attr : map.setIfUndefined(idmap.attrsH, _hashAttribution(attr), () => { - idmap.attrs.add(attr) - return attr - })) + idmap.attrs.has(attr) + ? attr + : map.setIfUndefined(idmap.attrsH, _hashAttribution(attr), () => { + idmap.attrs.add(attr) + return attr + })) export const createIdMap = () => new IdMap() @@ -358,4 +502,9 @@ export const createIdMap = () => new IdMap() * @param {IdSet | IdMap} exclude * @return {ISet} */ -export const diffIdMap = _diffSet +export const diffIdMap = (set, exclude) => { + const diffed = _diffSet(set, exclude) + diffed.attrs = set.attrs + diffed.attrsH = set.attrsH + return diffed +} diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 21efe141f..b17a0f6a9 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -303,6 +303,7 @@ export const diffIdSet = _diffSet * @function */ export const addToIdSet = (idSet, client, clock, length) => { + if (length === 0) return const idRanges = idSet.clients.get(client) if (idRanges) { idRanges.add(clock, length) diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 24388e32b..b8c7578d0 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,6 +1,7 @@ import * as t from 'lib0/testing' import * as am from '../src/utils/IdMap.js' import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js' +import * as YY from '../src/internals.js' /** * @template T @@ -115,6 +116,8 @@ export const testRepeatRandomDiffing = tc => { const e1 = am.diffIdMap(ds1, ds2) const e2 = am.diffIdMap(merged, ds2) compareIdmaps(e1, e2) + const copy = YY.decodeIdMap(YY.encodeIdMap(e1)) + compareIdmaps(e1, copy) } /** @@ -133,4 +136,6 @@ export const testRepeatRandomDiffing2 = tc => { const e2 = am.diffIdMap(am2, idsExclude) const excludedMerged = am.mergeIdMaps([e1, e2]) compareIdmaps(mergedExcluded, excludedMerged) + const copy = YY.decodeIdMap(YY.encodeIdMap(mergedExcluded)) + compareIdmaps(mergedExcluded, copy) } diff --git a/tests/testHelper.js b/tests/testHelper.js index 6e55c746c..9be857eab 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -8,7 +8,7 @@ import * as map from 'lib0/map' import * as Y from '../src/index.js' import * as math from 'lib0/math' import { - createIdSet, createIdMap, addToIdSet + createIdSet, createIdMap, addToIdSet, encodeIdMap } from '../src/internals.js' export * from '../src/index.js' @@ -313,7 +313,7 @@ export const init = (tc, { users = 5 } = {}, initTestObject) => { * @param {Y.IdSet} idSet2 */ export const compareIdSets = (idSet1, idSet2) => { - if (idSet1.clients.size !== idSet2.clients.size) return false + t.assert(idSet1.clients.size === idSet2.clients.size) for (const [client, _items1] of idSet1.clients.entries()) { const items1 = _items1.getIds() const items2 = idSet2.clients.get(client)?.getIds() @@ -349,13 +349,34 @@ const _idmapAttrsHas = (attrs, attr) => { */ export const _idmapAttrsEqual = (a, b) => a.length === b.length && a.every(v => _idmapAttrsHas(b, v)) +/** + * Ensure that all attributes exist. Also create a copy and compare it to the original. + * + * @template T + * @param {Y.IdMap} idmap + */ +export const validateIdMap = idmap => { + const copy = Y.createIdMap() + idmap.clients.forEach((ranges, client) => { + ranges.getIds().forEach(range => { + range.attrs.forEach(attr => { + t.assert(idmap.attrs.has(attr)) + t.assert(idmap.attrsH.get(attr.hash()) === attr) + copy.add(client, range.clock, range.len, range.attrs.slice()) + }) + }) + t.assert(copy.clients.get(client)?.getIds().length === ranges.getIds().length) + }) + t.assert(idmap.attrsH.size === idmap.attrs.size) +} + /** * @template T * @param {Y.IdMap} am1 * @param {Y.IdMap} am2 */ export const compareIdmaps = (am1, am2) => { - if (am1.clients.size !== am2.clients.size) return false + t.assert(am1.clients.size === am2.clients.size) for (const [client, _items1] of am1.clients.entries()) { const items1 = _items1.getIds() const items2 = am2.clients.get(client)?.getIds() @@ -366,7 +387,8 @@ export const compareIdmaps = (am1, am2) => { t.assert(di1.clock === di2.clock && di1.len === di2.len && _idmapAttrsEqual(di1.attrs, di2.attrs)) } } - return true + validateIdMap(am1) + validateIdMap(am2) } /** @@ -416,6 +438,7 @@ export const createRandomIdMap = (gen, clients, clockRange, attrChoices) => { } idMap.add(client, clockStart, len, attrs.map(v => Y.createAttribution('', v))) } + t.info(`Created IdMap with ${numOfOps} ranges and ${attrChoices.length} different attributes. Encoded size: ${encodeIdMap(idMap).byteLength}`) return idMap } From 065f268b0083b7bbc43901944f6df5307facb09b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Apr 2025 15:21:14 +0200 Subject: [PATCH 274/362] more renames (am=>idmap) --- tests/IdMap.tests.js | 30 +++++++++++++++--------------- tests/testHelper.js | 16 ++++++++-------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index b8c7578d0..215dffee1 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,5 +1,5 @@ import * as t from 'lib0/testing' -import * as am from '../src/utils/IdMap.js' +import * as idmap from '../src/utils/IdMap.js' import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js' import * as YY from '../src/internals.js' @@ -77,16 +77,16 @@ export const testRepeatMergingMultipleIdMaps = tc => { const clients = 4 const clockRange = 5 /** - * @type {Array>} + * @type {Array>} */ const sets = [] for (let i = 0; i < 3; i++) { sets.push(createRandomIdMap(tc.prng, clients, clockRange, [1, 2, 3])) } - const merged = am.mergeIdMaps(sets) - const mergedReverse = am.mergeIdMaps(sets.reverse()) + const merged = idmap.mergeIdMaps(sets) + const mergedReverse = idmap.mergeIdMaps(sets.reverse()) compareIdmaps(merged, mergedReverse) - const composed = am.createIdMap() + const composed = idmap.createIdMap() for (let iclient = 0; iclient < clients; iclient++) { for (let iclock = 0; iclock < clockRange + 42; iclock++) { const mergedHas = merged.has(new ID(iclient, iclock)) @@ -112,9 +112,9 @@ export const testRepeatRandomDiffing = tc => { const attrs = [1, 2, 3] const ds1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) const ds2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) - const merged = am.mergeIdMaps([ds1, ds2]) - const e1 = am.diffIdMap(ds1, ds2) - const e2 = am.diffIdMap(merged, ds2) + const merged = idmap.mergeIdMaps([ds1, ds2]) + const e1 = idmap.diffIdMap(ds1, ds2) + const e2 = idmap.diffIdMap(merged, ds2) compareIdmaps(e1, e2) const copy = YY.decodeIdMap(YY.encodeIdMap(e1)) compareIdmaps(e1, copy) @@ -127,14 +127,14 @@ export const testRepeatRandomDiffing2 = tc => { const clients = 4 const clockRange = 100 const attrs = [1, 2, 3] - const am1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) - const am2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const idmap1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const idmap2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) const idsExclude = createRandomIdSet(tc.prng, clients, clockRange) - const merged = am.mergeIdMaps([am1, am2]) - const mergedExcluded = am.diffIdMap(merged, idsExclude) - const e1 = am.diffIdMap(am1, idsExclude) - const e2 = am.diffIdMap(am2, idsExclude) - const excludedMerged = am.mergeIdMaps([e1, e2]) + const merged = idmap.mergeIdMaps([idmap1, idmap2]) + const mergedExcluded = idmap.diffIdMap(merged, idsExclude) + const e1 = idmap.diffIdMap(idmap1, idsExclude) + const e2 = idmap.diffIdMap(idmap2, idsExclude) + const excludedMerged = idmap.mergeIdMaps([e1, e2]) compareIdmaps(mergedExcluded, excludedMerged) const copy = YY.decodeIdMap(YY.encodeIdMap(mergedExcluded)) compareIdmaps(mergedExcluded, copy) diff --git a/tests/testHelper.js b/tests/testHelper.js index 9be857eab..85dea1fc2 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -372,14 +372,14 @@ export const validateIdMap = idmap => { /** * @template T - * @param {Y.IdMap} am1 - * @param {Y.IdMap} am2 + * @param {Y.IdMap} idmap1 + * @param {Y.IdMap} idmap2 */ -export const compareIdmaps = (am1, am2) => { - t.assert(am1.clients.size === am2.clients.size) - for (const [client, _items1] of am1.clients.entries()) { +export const compareIdmaps = (idmap1, idmap2) => { + t.assert(idmap1.clients.size === idmap2.clients.size) + for (const [client, _items1] of idmap1.clients.entries()) { const items1 = _items1.getIds() - const items2 = am2.clients.get(client)?.getIds() + const items2 = idmap2.clients.get(client)?.getIds() t.assert(items2 !== undefined && items1.length === items2.length) for (let i = 0; i < items1.length; i++) { const di1 = items1[i] @@ -387,8 +387,8 @@ export const compareIdmaps = (am1, am2) => { t.assert(di1.clock === di2.clock && di1.len === di2.len && _idmapAttrsEqual(di1.attrs, di2.attrs)) } } - validateIdMap(am1) - validateIdMap(am2) + validateIdMap(idmap1) + validateIdMap(idmap2) } /** From f78a7d009ea6c74f61ad18ca372e594d6650b128 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Apr 2025 15:33:09 +0200 Subject: [PATCH 275/362] more renames (ds=>idset) --- src/utils/IdMap.js | 12 ++++++------ src/utils/IdSet.js | 10 +++++----- src/utils/PermanentUserData.js | 6 +++--- src/utils/Snapshot.js | 8 ++++---- src/utils/UpdateEncoder.js | 20 ++++++++++---------- src/utils/encoding.js | 14 +++++++------- src/utils/updates.js | 10 +++++----- tests/IdMap.tests.js | 10 +++++----- tests/IdSet.tests.js | 6 +++--- tests/testHelper.js | 10 +++++----- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 8459a6eef..206c89abb 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -1,7 +1,7 @@ import { _diffSet, findIndexInIdRanges, - DSDecoderV1, DSDecoderV2, DSEncoderV1, DSEncoderV2, IdSet, ID // eslint-disable-line + DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' @@ -342,7 +342,7 @@ export class IdMap { * written. Attribute.names are referenced by id. Attributes themselfs are also referenced by id. * * @template Attr - * @param {DSEncoderV1 | DSEncoderV2} encoder + * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder * @param {IdMap} idmap * * @private @@ -364,7 +364,7 @@ export const writeIdMap = (encoder, idmap) => { .sort((a, b) => a[0] - b[0]) .forEach(([client, _idRanges]) => { const attrRanges = _idRanges.getIds() - encoder.resetDsCurVal() + encoder.resetIdSetCurVal() const diff = client - lastWrittenClientId encoding.writeVarUint(encoder.restEncoder, diff) lastWrittenClientId = client @@ -374,8 +374,8 @@ export const writeIdMap = (encoder, idmap) => { const item = attrRanges[i] const attrs = item.attrs const attrLen = attrs.length - encoder.writeDsClock(item.clock) - encoder.writeDsLen(item.len) + encoder.writeIdSetClock(item.clock) + encoder.writeIdSetLen(item.len) encoding.writeVarUint(encoder.restEncoder, attrLen) for (let j = 0; j < attrLen; j++) { const attr = attrs[j] @@ -407,7 +407,7 @@ export const writeIdMap = (encoder, idmap) => { * @param {IdMap} idmap */ export const encodeIdMap = idmap => { - const encoder = new DSEncoderV2() + const encoder = new IdSetEncoderV2() writeIdMap(encoder, idmap) return encoder.toUint8Array() } diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index b17a0f6a9..39c26d566 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -6,7 +6,7 @@ import { UpdateEncoderV2, IdMap, AttrRanges, - AbstractStruct, DSDecoderV1, DSEncoderV1, DSDecoderV2, DSEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line + AbstractStruct, DSDecoderV1, IdSetEncoderV1, DSDecoderV2, IdSetEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' @@ -358,7 +358,7 @@ export const createDeleteSetFromStructStore = ss => { } /** - * @param {DSEncoderV1 | DSEncoderV2} encoder + * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder * @param {IdSet} idSet * * @private @@ -371,14 +371,14 @@ export const writeIdSet = (encoder, idSet) => { .sort((a, b) => b[0] - a[0]) .forEach(([client, _idRanges]) => { const idRanges = _idRanges.getIds() - encoder.resetDsCurVal() + encoder.resetIdSetCurVal() encoding.writeVarUint(encoder.restEncoder, client) const len = idRanges.length encoding.writeVarUint(encoder.restEncoder, len) for (let i = 0; i < len; i++) { const item = idRanges[i] - encoder.writeDsClock(item.clock) - encoder.writeDsLen(item.len) + encoder.writeIdSetClock(item.clock) + encoder.writeIdSetLen(item.len) } }) } diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js index 683e77d33..fca1b663a 100644 --- a/src/utils/PermanentUserData.js +++ b/src/utils/PermanentUserData.js @@ -5,7 +5,7 @@ import { writeIdSet, createIdSet, mergeIdSets, - DSEncoderV1, DSDecoderV1, ID, IdSet, YArrayEvent, Transaction, Doc // eslint-disable-line + IdSetEncoderV1, DSDecoderV1, ID, IdSet, YArrayEvent, Transaction, Doc // eslint-disable-line } from '../internals.js' import * as decoding from 'lib0/decoding' @@ -95,7 +95,7 @@ export class PermanentUserData { user.get('ids').push([clientid]) } }) - const encoder = new DSEncoderV1() + const encoder = new IdSetEncoderV1() const ds = this.dss.get(userDescription) if (ds) { writeIdSet(encoder, ds) @@ -109,7 +109,7 @@ export class PermanentUserData { const yds = user.get('ds') const ds = transaction.deleteSet if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) { - const encoder = new DSEncoderV1() + const encoder = new IdSetEncoderV1() writeIdSet(encoder, ds) yds.push([encoder.toUint8Array()]) } diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index a381ed78a..fd21a37b4 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -15,7 +15,7 @@ import { applyUpdateV2, LazyStructReader, equalIdSets, - UpdateDecoderV1, UpdateDecoderV2, DSEncoderV1, DSEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, IdSet, Item, // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, IdSetEncoderV1, IdSetEncoderV2, DSDecoderV1, DSDecoderV2, Transaction, Doc, IdSet, Item, // eslint-disable-line mergeIdSets } from '../internals.js' @@ -63,10 +63,10 @@ export const equalSnapshots = (snap1, snap2) => { /** * @param {Snapshot} snapshot - * @param {DSEncoderV1 | DSEncoderV2} [encoder] + * @param {IdSetEncoderV1 | IdSetEncoderV2} [encoder] * @return {Uint8Array} */ -export const encodeSnapshotV2 = (snapshot, encoder = new DSEncoderV2()) => { +export const encodeSnapshotV2 = (snapshot, encoder = new IdSetEncoderV2()) => { writeIdSet(encoder, snapshot.ds) writeStateVector(encoder, snapshot.sv) return encoder.toUint8Array() @@ -76,7 +76,7 @@ export const encodeSnapshotV2 = (snapshot, encoder = new DSEncoderV2()) => { * @param {Snapshot} snapshot * @return {Uint8Array} */ -export const encodeSnapshot = snapshot => encodeSnapshotV2(snapshot, new DSEncoderV1()) +export const encodeSnapshot = snapshot => encodeSnapshotV2(snapshot, new IdSetEncoderV1()) /** * @param {Uint8Array} buf diff --git a/src/utils/UpdateEncoder.js b/src/utils/UpdateEncoder.js index 2b742beea..e23b9d020 100644 --- a/src/utils/UpdateEncoder.js +++ b/src/utils/UpdateEncoder.js @@ -5,7 +5,7 @@ import { ID // eslint-disable-line } from '../internals.js' -export class DSEncoderV1 { +export class IdSetEncoderV1 { constructor () { this.restEncoder = encoding.createEncoder() } @@ -14,26 +14,26 @@ export class DSEncoderV1 { return encoding.toUint8Array(this.restEncoder) } - resetDsCurVal () { + resetIdSetCurVal () { // nop } /** * @param {number} clock */ - writeDsClock (clock) { + writeIdSetClock (clock) { encoding.writeVarUint(this.restEncoder, clock) } /** * @param {number} len */ - writeDsLen (len) { + writeIdSetLen (len) { encoding.writeVarUint(this.restEncoder, len) } } -export class UpdateEncoderV1 extends DSEncoderV1 { +export class UpdateEncoderV1 extends IdSetEncoderV1 { /** * @param {ID} id */ @@ -124,7 +124,7 @@ export class UpdateEncoderV1 extends DSEncoderV1 { } } -export class DSEncoderV2 { +export class IdSetEncoderV2 { constructor () { this.restEncoder = encoding.createEncoder() // encodes all the rest / non-optimized this.dsCurrVal = 0 @@ -134,14 +134,14 @@ export class DSEncoderV2 { return encoding.toUint8Array(this.restEncoder) } - resetDsCurVal () { + resetIdSetCurVal () { this.dsCurrVal = 0 } /** * @param {number} clock */ - writeDsClock (clock) { + writeIdSetClock (clock) { const diff = clock - this.dsCurrVal this.dsCurrVal = clock encoding.writeVarUint(this.restEncoder, diff) @@ -150,7 +150,7 @@ export class DSEncoderV2 { /** * @param {number} len */ - writeDsLen (len) { + writeIdSetLen (len) { if (len === 0) { error.unexpectedCase() } @@ -159,7 +159,7 @@ export class DSEncoderV2 { } } -export class UpdateEncoderV2 extends DSEncoderV2 { +export class UpdateEncoderV2 extends IdSetEncoderV2 { constructor () { super() /** diff --git a/src/utils/encoding.js b/src/utils/encoding.js index f2a5a8f4e..621a58752 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -27,9 +27,9 @@ import { UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, - DSEncoderV2, + IdSetEncoderV2, DSDecoderV1, - DSEncoderV1, + IdSetEncoderV1, mergeUpdates, mergeUpdatesV2, Skip, @@ -613,7 +613,7 @@ export const readStateVector = decoder => { export const decodeStateVector = decodedState => readStateVector(new DSDecoderV1(decoding.createDecoder(decodedState))) /** - * @param {DSEncoderV1 | DSEncoderV2} encoder + * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder * @param {Map} sv * @function */ @@ -627,7 +627,7 @@ export const writeStateVector = (encoder, sv) => { } /** - * @param {DSEncoderV1 | DSEncoderV2} encoder + * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder * @param {Doc} doc * * @function @@ -638,12 +638,12 @@ export const writeDocumentStateVector = (encoder, doc) => writeStateVector(encod * Encode State as Uint8Array. * * @param {Doc|Map} doc - * @param {DSEncoderV1 | DSEncoderV2} [encoder] + * @param {IdSetEncoderV1 | IdSetEncoderV2} [encoder] * @return {Uint8Array} * * @function */ -export const encodeStateVectorV2 = (doc, encoder = new DSEncoderV2()) => { +export const encodeStateVectorV2 = (doc, encoder = new IdSetEncoderV2()) => { if (doc instanceof Map) { writeStateVector(encoder, doc) } else { @@ -660,4 +660,4 @@ export const encodeStateVectorV2 = (doc, encoder = new DSEncoderV2()) => { * * @function */ -export const encodeStateVector = doc => encodeStateVectorV2(doc, new DSEncoderV1()) +export const encodeStateVector = doc => encodeStateVectorV2(doc, new IdSetEncoderV1()) diff --git a/src/utils/updates.js b/src/utils/updates.js index ba69132cc..558c9c2c7 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -20,8 +20,8 @@ import { ContentType, createID, decodeStateVector, - DSEncoderV1, - DSEncoderV2, + IdSetEncoderV1, + IdSetEncoderV2, GC, Item, mergeIdSets, @@ -187,11 +187,11 @@ export const mergeUpdates = updates => mergeUpdatesV2(updates, UpdateDecoderV1, /** * @param {Uint8Array} update - * @param {typeof DSEncoderV1 | typeof DSEncoderV2} YEncoder + * @param {typeof IdSetEncoderV1 | typeof IdSetEncoderV2} YEncoder * @param {typeof UpdateDecoderV1 | typeof UpdateDecoderV2} YDecoder * @return {Uint8Array} */ -export const encodeStateVectorFromUpdateV2 = (update, YEncoder = DSEncoderV2, YDecoder = UpdateDecoderV2) => { +export const encodeStateVectorFromUpdateV2 = (update, YEncoder = IdSetEncoderV2, YDecoder = UpdateDecoderV2) => { const encoder = new YEncoder() const updateDecoder = new LazyStructReader(new YDecoder(decoding.createDecoder(update)), false) let curr = updateDecoder.curr @@ -243,7 +243,7 @@ export const encodeStateVectorFromUpdateV2 = (update, YEncoder = DSEncoderV2, YD * @param {Uint8Array} update * @return {Uint8Array} */ -export const encodeStateVectorFromUpdate = update => encodeStateVectorFromUpdateV2(update, DSEncoderV1, UpdateDecoderV1) +export const encodeStateVectorFromUpdate = update => encodeStateVectorFromUpdateV2(update, IdSetEncoderV1, UpdateDecoderV1) /** * @param {Uint8Array} update diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 215dffee1..1c21e9d36 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -110,11 +110,11 @@ export const testRepeatRandomDiffing = tc => { const clients = 4 const clockRange = 100 const attrs = [1, 2, 3] - const ds1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) - const ds2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) - const merged = idmap.mergeIdMaps([ds1, ds2]) - const e1 = idmap.diffIdMap(ds1, ds2) - const e2 = idmap.diffIdMap(merged, ds2) + const idset1 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const idset2 = createRandomIdMap(tc.prng, clients, clockRange, attrs) + const merged = idmap.mergeIdMaps([idset1, idset2]) + const e1 = idmap.diffIdMap(idset1, idset2) + const e2 = idmap.diffIdMap(merged, idset2) compareIdmaps(e1, e2) const copy = YY.decodeIdMap(YY.encodeIdMap(e1)) compareIdmaps(e1, copy) diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index afe990155..8078afefa 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -6,11 +6,11 @@ import { compareIdSets, createRandomIdSet, ID } from './testHelper.js' * @param {Array<[number, number, number]>} ops */ const simpleConstructIdSet = ops => { - const ds = d.createIdSet() + const idset = d.createIdSet() ops.forEach(op => { - d.addToIdSet(ds, op[0], op[1], op[2]) + d.addToIdSet(idset, op[0], op[1], op[2]) }) - return ds + return idset } /** diff --git a/tests/testHelper.js b/tests/testHelper.js index 85dea1fc2..da0eef70e 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -399,17 +399,17 @@ export const compareIdmaps = (idmap1, idmap2) => { export const createRandomIdSet = (gen, clients, clockRange) => { const maxOpLen = 5 const numOfOps = math.ceil((clients * clockRange) / maxOpLen) - const ds = createIdSet() + const idset = createIdSet() for (let i = 0; i < numOfOps; i++) { const client = prng.uint32(gen, 0, clients - 1) const clockStart = prng.uint32(gen, 0, clockRange) const len = prng.uint32(gen, 0, clockRange - clockStart) - addToIdSet(ds, client, clockStart, len) + addToIdSet(idset, client, clockStart, len) } - if (ds.clients.size === clients && clients > 1 && prng.bool(gen)) { - ds.clients.delete(prng.uint32(gen, 0, clients)) + if (idset.clients.size === clients && clients > 1 && prng.bool(gen)) { + idset.clients.delete(prng.uint32(gen, 0, clients)) } - return ds + return idset } /** From 5f5cf343a8b4749388f62009c023d28193d35f0b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 20 Apr 2025 19:28:36 +0200 Subject: [PATCH 276/362] implement base AttributionManager class and use in Y.Text --- package-lock.json | 31 ++++++----- package.json | 4 +- src/internals.js | 1 + src/types/YText.js | 97 +++++++++++++-------------------- src/utils/AttributionManager.js | 88 ++++++++++++++++++++++++++++++ src/utils/IdMap.js | 32 +++++++++-- tests/IdMap.tests.js | 4 +- 7 files changed, 176 insertions(+), 81 deletions(-) create mode 100644 src/utils/AttributionManager.js diff --git a/package-lock.json b/package-lock.json index 1388ff884..8fdb6e199 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,14 +13,14 @@ "y-protocols": "^1.0.5" }, "devDependencies": { - "@types/node": "^18.15.5", + "@types/node": "^22.14.1", "concurrently": "^3.6.1", "jsdoc": "^3.6.7", "markdownlint-cli": "^0.41.0", "rollup": "^4.37.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^4.9.5", + "typescript": "^5.8.3", "yjs": "." }, "engines": { @@ -557,12 +557,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.19.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.15.tgz", - "integrity": "sha512-AMZ2UWx+woHNfM11PyAEQmfSxi05jm9OlkxczuHeEqmvwPkYj6MWv44gbzDPefYOLysTOFyI3ziiy2ONmUZfpA==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/acorn": { @@ -4547,16 +4548,17 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uc.micro": { @@ -4587,10 +4589,11 @@ "dev": true }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/uri-js": { "version": "4.4.1", diff --git a/package.json b/package.json index 22607b6ab..46686830a 100644 --- a/package.json +++ b/package.json @@ -90,14 +90,14 @@ "y-protocols": "^1.0.5" }, "devDependencies": { - "@types/node": "^18.15.5", + "@types/node": "^22.14.1", "concurrently": "^3.6.1", "jsdoc": "^3.6.7", "markdownlint-cli": "^0.41.0", "rollup": "^4.37.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^4.9.5", + "typescript": "^5.8.3", "yjs": "." }, "engines": { diff --git a/src/internals.js b/src/internals.js index f7005afd2..6741e0e03 100644 --- a/src/internals.js +++ b/src/internals.js @@ -41,3 +41,4 @@ export * from './structs/ContentType.js' export * from './structs/Item.js' export * from './structs/Skip.js' export * from './utils/IdMap.js' +export * from './utils/AttributionManager.js' diff --git a/src/types/YText.js b/src/types/YText.js index 0a70f371d..053f23a2d 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -26,8 +26,7 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, IdMap, // eslint-disable-line - snapshot + noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -999,85 +998,63 @@ export class YText extends AbstractType { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @param {IdMap} [idMap] - * @param {Doc} [prevYdoc] + * @param {AbstractAttributionManager} am * @return {import('../utils/Delta.js').Delta} The Delta representation of this type. * * @public */ - getContent (idMap, prevYdoc) { + getContent (am = noAttributionsManager) { this.doc ?? warnPrematureAccess() - const prevSnapshot = prevYdoc ? snapshot(prevYdoc) : null const d = delta.create() - /** - * @type {{ [key: string]: any }} - */ - const currentAttributes = {} - const doc = /** @type {Doc} */ (this.doc) - const computeContent = () => { - let n = this._start - while (n !== null) { - switch (n.content.constructor) { - case ContentString: { - const cur = currentAttributes.get('ychange') - if (snapshot !== undefined && !isVisible(n, snapshot)) { - if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') { - packStr() - currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' }) + for (let item = this._start; item !== null; item = item.right) { + const cs = am.getContent(item) + for (let i = 0; i < cs.length; i++) { + const { content, deleted, attrs } = cs[i] + /** + * @type {{ [key: string]: any }?} + */ + let attributions = null + if (attrs != null) { + attributions = {} + attrs.forEach(attr => { + switch (attr.name) { + case '_insertedBy': + case '_deletedBy': + case '_suggestedBy': { + const as = /** @type {any} */ (attributions) + const ls = as[attr.name] = as[attr.name] ?? [] + ls.push(attr.val) + break } - } else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) { - if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') { - packStr() - currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' }) + default: { + if (attr.name[0] !== '_') { + /** @type {any} */ (attributions)[attr.name] = attr.val + } } - } else if (cur !== undefined) { - packStr() - currentAttributes.delete('ychange') } - str += /** @type {ContentString} */ (n.content).str + }) + } + switch (content.constructor) { + case ContentString: { + d.insert(/** @type {ContentString} */ (content).str, {}, attributions) break } case ContentType: case ContentEmbed: { - packStr() - /** - * @type {Object} - */ - const op = { - insert: n.content.getContent()[0] - } - if (currentAttributes.size > 0) { - const attrs = /** @type {Object} */ ({}) - op.attributes = attrs - currentAttributes.forEach((value, key) => { - attrs[key] = value - }) - } - ops.push(op) + d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], {}, attributions) break } case ContentFormat: - if (isVisible(n, snapshot)) { - packStr() - updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content)) + if (attributions != null) { + attributions.formattedBy = (deleted ? attributions.deletedBy : attributions.insertedBy) ?? [] + delete attributions.deletedBy + delete attributions.insertedBy + d.useAttribution(attributions) } break } - n = n.right } } - if (prevSnapshot) { - // snapshots are merged again after the transaction, so we need to keep the - // transaction alive until we are done - transact(doc, transaction => { - if (prevSnapshot) { - splitSnapshotAffectedStructs(transaction, prevSnapshot) - } - computeContent() - }, 'cleanup') - } else { - computeContent() - } return d.done() } diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js new file mode 100644 index 000000000..e3f42a1b1 --- /dev/null +++ b/src/utils/AttributionManager.js @@ -0,0 +1,88 @@ +import { + Item, AbstractContent, IdMap // eslint-disable-line +} from '../internals.js' + +import * as error from 'lib0/error' + +/** + * @template T + */ +export class AttributedContent { + /** + * @param {AbstractContent} content + * @param {boolean} deleted + * @param {Array> | null} attrs + */ + constructor (content, deleted, attrs) { + this.content = content + this.deleted = deleted + this.attrs = attrs + } +} + +/** + * Abstract class for associating Attributions to content / changes + */ +export class AbstractAttributionManager { + /** + * @param {Item} _item + * @return {Array>} + */ + getContent (_item) { + error.methodUnimplemented() + } +} + +/** + * Abstract class for associating Attributions to content / changes + * + * @implements AbstractAttributionManager + */ +export class TwosetAttributionManager { + /** + * @param {IdMap} inserts + * @param {IdMap} deletes + */ + constructor (inserts, deletes) { + this.inserts = inserts + this.deletes = deletes + } + + /** + * @param {Item} item + * @return {Array>} + */ + getContent (item) { + const deleted = item.deleted + const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) + let content = slice.length === 1 ? item.content : item.content.copy() + let res = slice.map(s => { + const c = content + if (s.len < c.getLength()) { + content = c.splice(s.len) + } + return new AttributedContent(c, deleted, s.attrs) + }) + if (deleted) { + res = res.filter(s => s.attrs != null) + } + return res + } +} + +/** + * Abstract class for associating Attributions to content / changes + * + * @implements AbstractAttributionManager + */ +export class NoAttributionsManager { + /** + * @param {Item} item + * @return {Array>} + */ + getContent (item) { + return item.deleted ? [] : [new AttributedContent(item.content, item.deleted, null)] + } +} + +export const noAttributionsManager = new NoAttributionsManager() diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 206c89abb..0e0d3197a 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -105,6 +105,11 @@ export class AttrRange { } } +/** + * @template Attrs + * @typedef {{ clock: number, len: number, attrs: Array>? }} MaybeAttrRange + */ + /** * @template Attrs */ @@ -287,12 +292,18 @@ export class IdMap { } /** + * Return attributions for a slice of ids. + * * @param {ID} id * @param {number} len - * @return {Array>?} + * @return {Array>} */ slice (id, len) { const dr = this.clients.get(id.client) + /** + * @type {Array>} + */ + const res = [] if (dr) { /** * @type {Array>} @@ -300,7 +311,7 @@ export class IdMap { const ranges = dr.getIds() let index = findIndexInIdRanges(ranges, id.clock) if (index !== null) { - const res = [] + let prev = null while (index < ranges.length) { let r = ranges[index] if (r.clock < id.clock) { @@ -310,13 +321,26 @@ export class IdMap { r = new AttrRange(r.clock, id.clock + len - r.clock, r.attrs) } if (r.len <= 0) break + const prevEnd = prev != null ? prev.clock + prev.len : index + if (prevEnd < index) { + res.push(/** @type {MaybeAttrRange} */ (new AttrRange(prevEnd, index - prevEnd, /** @type {any} */ (null)))) + } + prev = r res.push(r) index++ } - return res } } - return null + if (res.length > 0) { + const last = res[res.length - 1] + const end = last.clock + last.len + if (end < id.clock + len) { + res.push(new AttrRange(end, id.clock + len - end, [])) + } + } else { + res.push(new AttrRange(id.clock, len, [])) + } + return res } /** diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 1c21e9d36..68a595ca6 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -95,7 +95,9 @@ export const testRepeatMergingMultipleIdMaps = tc => { const mergedAttrs = merged.slice(new ID(iclient, iclock), 1) if (mergedAttrs) { mergedAttrs.forEach(a => { - composed.add(iclient, a.clock, a.len, a.attrs) + if (a.attrs != null) { + composed.add(iclient, a.clock, a.len, a.attrs) + } }) } } From 2e2968e71bf77946794350813392923cd9ea1779 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 21 Apr 2025 01:13:41 +0200 Subject: [PATCH 277/362] attributions and fixes to idmap --- src/types/YText.js | 20 +++++++++++++------- src/utils/Delta.js | 8 ++++---- src/utils/IdMap.js | 29 ++++++++++++++++++----------- tests/IdMap.tests.js | 14 ++++++-------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 053f23a2d..f9d363945 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1016,11 +1016,12 @@ export class YText extends AbstractType { let attributions = null if (attrs != null) { attributions = {} + attributions.changeType = deleted ? 'delete' : 'insert' attrs.forEach(attr => { switch (attr.name) { - case '_insertedBy': - case '_deletedBy': - case '_suggestedBy': { + case 'insertedBy': + case 'deletedBy': + case 'suggestedBy': { const as = /** @type {any} */ (attributions) const ls = as[attr.name] = as[attr.name] ?? [] ls.push(attr.val) @@ -1046,10 +1047,15 @@ export class YText extends AbstractType { } case ContentFormat: if (attributions != null) { - attributions.formattedBy = (deleted ? attributions.deletedBy : attributions.insertedBy) ?? [] - delete attributions.deletedBy - delete attributions.insertedBy - d.useAttribution(attributions) + if (deleted) { + d.useAttribution(null) + } else { + attributions.formattedBy = (deleted ? attributions.deletedBy : attributions.insertedBy) ?? [] + attributions.changeType = 'format' + delete attributions.deletedBy + delete attributions.insertedBy + d.useAttribution(attributions) + } } break } diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 60f12c4bd..c1c5b5253 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -106,22 +106,22 @@ export class DeltaBuilder extends Delta { } /** - * @param {FormattingAttributes} attributes + * @param {FormattingAttributes?} attributes * @return {this} */ useAttributes (attributes) { if (this._useAttributes === attributes) return this - this._useAttributes = object.assign({}, attributes) + this._useAttributes = attributes && object.assign({}, attributes) if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null return this } /** - * @param {Attribution} attribution + * @param {Attribution?} attribution */ useAttribution (attribution) { if (this._useAttribution === attribution) return this - this._useAttribution = object.assign({}, attribution) + this._useAttribution = attribution && object.assign({}, attribution) if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null return this } diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 0e0d3197a..522fb81a8 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -110,6 +110,16 @@ export class AttrRange { * @typedef {{ clock: number, len: number, attrs: Array>? }} MaybeAttrRange */ +/** + * @template Attrs + * + * @param {number} clock + * @param {number} len + * @param {Array>?} attrs + * @return {MaybeAttrRange} + */ +export const createMaybeAttrRange = (clock, len, attrs) => new AttrRange(clock, len, /** @type {any} */ (attrs)) + /** * @template Attrs */ @@ -238,21 +248,18 @@ export const mergeIdMaps = ams => { if (!merged.clients.has(client)) { // Write all missing keys from current set and all following. // If merged already contains `client` current ds has already been added. - const ids = rangesLeft.getIds().slice() + let ids = rangesLeft.getIds().slice() for (let i = amsI + 1; i < ams.length; i++) { const nextIds = ams[i].clients.get(client) if (nextIds) { array.appendTo(ids, nextIds.getIds()) } } - ids.forEach(id => { - // @ts-ignore - id.attrs = id.attrs.map(attr => - map.setIfUndefined(attrMapper, attr, () => - _ensureAttrs(merged, [attr])[0] - ) + ids = ids.map(id => new AttrRange(id.clock, id.len, id.attrs.map(attr => + map.setIfUndefined(attrMapper, attr, () => + _ensureAttrs(merged, [attr])[0] ) - }) + ))) merged.clients.set(client, new AttrRanges(ids)) } }) @@ -323,7 +330,7 @@ export class IdMap { if (r.len <= 0) break const prevEnd = prev != null ? prev.clock + prev.len : index if (prevEnd < index) { - res.push(/** @type {MaybeAttrRange} */ (new AttrRange(prevEnd, index - prevEnd, /** @type {any} */ (null)))) + res.push(createMaybeAttrRange(prevEnd, index - prevEnd, null)) } prev = r res.push(r) @@ -335,10 +342,10 @@ export class IdMap { const last = res[res.length - 1] const end = last.clock + last.len if (end < id.clock + len) { - res.push(new AttrRange(end, id.clock + len - end, [])) + res.push(createMaybeAttrRange(end, id.clock + len - end, null)) } } else { - res.push(new AttrRange(id.clock, len, [])) + res.push(createMaybeAttrRange(id.clock, len, null)) } return res } diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 68a595ca6..79809e2e1 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,6 +1,6 @@ import * as t from 'lib0/testing' import * as idmap from '../src/utils/IdMap.js' -import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js' +import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution, validateIdMap } from './testHelper.js' import * as YY from '../src/internals.js' /** @@ -93,13 +93,11 @@ export const testRepeatMergingMultipleIdMaps = tc => { const oneHas = sets.some(ids => ids.has(new ID(iclient, iclock))) t.assert(mergedHas === oneHas) const mergedAttrs = merged.slice(new ID(iclient, iclock), 1) - if (mergedAttrs) { - mergedAttrs.forEach(a => { - if (a.attrs != null) { - composed.add(iclient, a.clock, a.len, a.attrs) - } - }) - } + mergedAttrs.forEach(a => { + if (a.attrs != null) { + composed.add(iclient, a.clock, a.len, a.attrs) + } + }) } } compareIdmaps(merged, composed) From 6671071213fe1d27c4a0b343c262f2981fe9355a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 21 Apr 2025 02:00:14 +0200 Subject: [PATCH 278/362] first test case for attributions --- src/utils/Delta.js | 41 +++++++++++++++++++++++++++++++++++++++++ src/utils/IdMap.js | 26 ++++++++++++++++++++++++++ tests/y-text.tests.js | 19 +++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/src/utils/Delta.js b/src/utils/Delta.js index c1c5b5253..175b047c1 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -1,4 +1,5 @@ import * as object from 'lib0/object' +import * as fun from 'lib0/function' /** * @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp @@ -71,6 +72,46 @@ export class Delta { this.ops = [] } + /** + * @param {Delta} d + * @return {boolean} + */ + equals (d) { + return this.ops.length === d.ops.length && this.ops.every((op, i) => { + const dop = d.ops[i] + if (op.constructor !== dop.constructor) return false + switch (op.constructor) { + case DeleteOp: { + if (/** @type {DeleteOp} */ (op).delete !== /** @type {DeleteOp} */ (dop).delete) { + return false + } + break + } + case InsertOp: { + if ( + !fun.equalityDeep(/** @type {InsertOp} */ (op).insert, /** @type {InsertOp} */ (dop).insert) + || !fun.equalityDeep(/** @type {InsertOp} */ (op).attributes, /** @type {InsertOp} */ (dop).attributes) + || !fun.equalityDeep(/** @type {InsertOp} */ (op).attribution, /** @type {InsertOp} */ (dop).attribution) + ) { + return false + } + break + } + case RetainOp: { + if ( + /** @type {RetainOp} */ (op).retain !== /** @type {RetainOp} */ (dop).retain + || !fun.equalityDeep(/** @type {RetainOp} */ (op).attributes, /** @type {RetainOp} */ (dop).attributes) + || !fun.equalityDeep(/** @type {RetainOp} */ (op).attribution, /** @type {RetainOp} */ (dop).attribution) + ) { + return false + } + break + } + } + return true + }) + } + toJSON () { return { ops: this.ops.map(o => o.toJSON()) } } diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 522fb81a8..820f3fa10 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -267,6 +267,32 @@ export const mergeIdMaps = ams => { return merged } +/** + * @param {IdSet} idset + * @param {Array>} attrs + */ +export const createIdMapFromIdSet = (idset, attrs) => { + const idmap = createIdMap() + // map attrs to idmap + attrs = _ensureAttrs(idmap, attrs) + // filter out duplicates + /** + * @type {Array>} + */ + const checkedAttrs = [] + attrs.forEach(attr => { + if (!idmapAttrsHas(checkedAttrs, attr)) { + checkedAttrs.push(attr) + } + }) + idset.clients.forEach((ranges, client) => { + const attrRanges = new AttrRanges(ranges.getIds().map(range => new AttrRange(range.clock, range.len, checkedAttrs))) + attrRanges.sorted = true // is sorted because idset is sorted + idmap.clients.set(client, attrRanges) + }) + return idmap +} + /** * @template Attrs */ diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 4b9c6d2e9..37c477994 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2,6 +2,8 @@ import * as Y from './testHelper.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' +import * as delta from '../src/utils/Delta.js' +import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager } from 'yjs/internals' const { init, compare } = Y @@ -2299,6 +2301,23 @@ export const testDeleteFormatting = _tc => { t.compare(text2.toDelta(), expected) } +/** + * @param {t.TestCase} tc + */ +export const testAttributedContent = tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + ytext.insert(0, 'Hello World!') + let am = noAttributionsManager + ydoc.on('afterTransaction', tr => { + am = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, [])) + }) + ytext.applyDelta([{ retain: 6 }, { delete: 5 }, { insert: 'attributions' }]) + const attributedContent = ytext.getContent(am) + t.assert(attributedContent.equals(delta.create().retain(6).insert('World', {}, { type: 'delete' }).insert('attributions', {}, { type: 'insert' }))) + debugger +} + // RANDOM TESTS let charCounter = 0 From 3c3769939237209825f7027d90b57303931a7feb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 21 Apr 2025 15:59:03 +0200 Subject: [PATCH 279/362] basic attribution test working --- src/types/YText.js | 4 ++-- src/utils/Delta.js | 22 +++++++++++++++------- tests/y-text.tests.js | 5 +++-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index f9d363945..b42a766c6 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1037,12 +1037,12 @@ export class YText extends AbstractType { } switch (content.constructor) { case ContentString: { - d.insert(/** @type {ContentString} */ (content).str, {}, attributions) + d.insert(/** @type {ContentString} */ (content).str, null, attributions) break } case ContentType: case ContentEmbed: { - d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], {}, attributions) + d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions) break } case ContentFormat: diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 175b047c1..9f31fd0ad 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -10,6 +10,8 @@ import * as fun from 'lib0/function' */ /** + * @todo specify this better + * * @typedef {Object} Attribution * @property {boolean} [Attribution.isDeleted] * @property {boolean} [Attribution.isAdded] @@ -124,7 +126,11 @@ export class Delta { * @param {T | null} a * @param {T | null} b */ -const mergeAttrs = (a, b) => a == null ? b : (b == null ? a : object.assign({}, a, b)) +const mergeAttrs = (a, b) => { + const merged = a == null ? b : (b == null ? a : object.assign({}, a, b)) + if (object.isEmpty(merged ?? {})) { return null } + return merged +} export class DeltaBuilder extends Delta { constructor () { @@ -153,7 +159,6 @@ export class DeltaBuilder extends Delta { useAttributes (attributes) { if (this._useAttributes === attributes) return this this._useAttributes = attributes && object.assign({}, attributes) - if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null return this } @@ -163,7 +168,6 @@ export class DeltaBuilder extends Delta { useAttribution (attribution) { if (this._useAttribution === attribution) return this this._useAttribution = attribution && object.assign({}, attribution) - if (this._lastOp?.constructor !== DeleteOp) this._lastOp = null return this } @@ -174,10 +178,12 @@ export class DeltaBuilder extends Delta { * @return {this} */ insert (insert, attributes = null, attribution = null) { - if (attributes === null && attribution === null && this._lastOp instanceof InsertOp) { + const mergedAttributes = mergeAttrs(this._useAttributes, attributes) + const mergedAttribution = mergeAttrs(this._useAttribution, attribution) + if (this._lastOp instanceof InsertOp && fun.equalityDeep(mergedAttributes, this._lastOp.attributes) && fun.equalityDeep(mergedAttribution, this._lastOp.attribution)) { this._lastOp.insert += insert } else { - this.ops.push(this._lastOp = new InsertOp(insert, mergeAttrs(this._useAttributes, attributes), mergeAttrs(this._useAttribution, attribution))) + this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) } return this } @@ -189,10 +195,12 @@ export class DeltaBuilder extends Delta { * @return {this} */ retain (retain, attributes = null, attribution = null) { - if (attributes === null && attribution === null && this._lastOp instanceof RetainOp) { + const mergedAttributes = mergeAttrs(this._useAttributes, attributes) + const mergedAttribution = mergeAttrs(this._useAttribution, attribution) + if (this._lastOp instanceof RetainOp && fun.equalityDeep(mergedAttributes, this._lastOp.attributes) && fun.equalityDeep(mergedAttribution, this._lastOp.attribution)) { this._lastOp.retain += retain } else { - this.ops.push(this._lastOp = new RetainOp(retain, mergeAttrs(this._useAttributes, attributes), mergeAttrs(this._useAttribution, attribution))) + this.ops.push(this._lastOp = new RetainOp(retain, mergedAttributes, mergedAttribution)) } return this } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 37c477994..c92c248a8 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2305,7 +2305,7 @@ export const testDeleteFormatting = _tc => { * @param {t.TestCase} tc */ export const testAttributedContent = tc => { - const ydoc = new Y.Doc() + const ydoc = new Y.Doc({ gc: false }) const ytext = ydoc.getText() ytext.insert(0, 'Hello World!') let am = noAttributionsManager @@ -2314,7 +2314,8 @@ export const testAttributedContent = tc => { }) ytext.applyDelta([{ retain: 6 }, { delete: 5 }, { insert: 'attributions' }]) const attributedContent = ytext.getContent(am) - t.assert(attributedContent.equals(delta.create().retain(6).insert('World', {}, { type: 'delete' }).insert('attributions', {}, { type: 'insert' }))) + const expectedContent = delta.create().insert('Hello ').insert('World', {}, { changeType: 'delete' }).insert('attributions', {}, { changeType: 'insert' }).insert('!') + t.assert(attributedContent.equals(expectedContent)) debugger } From 7de9476e27ca9a17d8a5fb56494f40ee5b5538ad Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 21 Apr 2025 18:27:18 +0200 Subject: [PATCH 280/362] attribution tests for adding and removing attributes --- src/types/YText.js | 50 ++++++++++++++++++++++++++++++++----------- src/utils/Delta.js | 36 +++++++++++++++---------------- src/utils/IdMap.js | 2 ++ tests/y-text.tests.js | 23 ++++++++++++++------ 4 files changed, 74 insertions(+), 37 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index b42a766c6..74554cea6 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1011,18 +1011,22 @@ export class YText extends AbstractType { for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] /** - * @type {{ [key: string]: any }?} + * @type {import('../utils/Delta.js').Attribution?} */ let attributions = null if (attrs != null) { attributions = {} - attributions.changeType = deleted ? 'delete' : 'insert' + if (deleted) { + attributions.delete = [] + } else { + attributions.insert = [] + } attrs.forEach(attr => { switch (attr.name) { - case 'insertedBy': - case 'deletedBy': - case 'suggestedBy': { - const as = /** @type {any} */ (attributions) + case 'insert': + case 'delete': + case 'suggest': { + const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions) const ls = as[attr.name] = as[attr.name] ?? [] ls.push(attr.val) break @@ -1046,15 +1050,37 @@ export class YText extends AbstractType { break } case ContentFormat: + const contentFormat = /** @type {ContentFormat} */ (content) if (attributions != null) { - if (deleted) { + /** + * @type {import('../utils/Delta.js').Attribution} + */ + const formattingAttributions = object.assign({}, d.usedAttribution) + const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {})) + if (contentFormat.value === null) { + delete attributesChanged[contentFormat.key] + } else { + const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] + by.push(...((deleted ? attributions.delete : attributions.insert) ?? [])) + const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) + if (attributedAt) formattingAttributions.attributedAt = attributedAt + } + if (object.isEmpty(attributesChanged)) { d.useAttribution(null) } else { - attributions.formattedBy = (deleted ? attributions.deletedBy : attributions.insertedBy) ?? [] - attributions.changeType = 'format' - delete attributions.deletedBy - delete attributions.insertedBy - d.useAttribution(attributions) + const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) + if (attributedAt != null) formattingAttributions.attributedAt = attributedAt + d.useAttribution(formattingAttributions) + } + } + if (!deleted) { + const currAttrs = d.usedAttributes + if (contentFormat.value == null) { + const nextAttrs = object.assign({}, currAttrs) + delete nextAttrs[contentFormat.key] + d.useAttributes(nextAttrs) + } else { + d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) } } break diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 9f31fd0ad..54712c194 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -10,13 +10,15 @@ import * as fun from 'lib0/function' */ /** - * @todo specify this better - * * @typedef {Object} Attribution - * @property {boolean} [Attribution.isDeleted] - * @property {boolean} [Attribution.isAdded] - * @property {string} [Attribution.creator] - * @property {number} [Attribution.timestamp] + * @property {Array} [Attribution.insert] + * @property {number} [Attribution.insertedAt] + * @property {Array} [Attribution.suggest] + * @property {number} [Attribution.suggestedAt] + * @property {Array} [Attribution.delete] + * @property {number} [Attribution.deletedAt] + * @property {{ [key: string]: Array }} [Attribution.attributes] + * @property {number} [Attribution.attributedAt] */ export class InsertOp { @@ -136,15 +138,13 @@ export class DeltaBuilder extends Delta { constructor () { super() /** - * @private * @type {FormattingAttributes?} */ - this._useAttributes = null + this.usedAttributes = null /** - * @private * @type {Attribution?} */ - this._useAttribution = null + this.usedAttribution = null /** * @private * @type {DeltaOp?} @@ -157,8 +157,8 @@ export class DeltaBuilder extends Delta { * @return {this} */ useAttributes (attributes) { - if (this._useAttributes === attributes) return this - this._useAttributes = attributes && object.assign({}, attributes) + if (this.usedAttributes === attributes) return this + this.usedAttributes = attributes && object.assign({}, attributes) return this } @@ -166,8 +166,8 @@ export class DeltaBuilder extends Delta { * @param {Attribution?} attribution */ useAttribution (attribution) { - if (this._useAttribution === attribution) return this - this._useAttribution = attribution && object.assign({}, attribution) + if (this.usedAttribution === attribution) return this + this.usedAttribution = attribution && object.assign({}, attribution) return this } @@ -178,8 +178,8 @@ export class DeltaBuilder extends Delta { * @return {this} */ insert (insert, attributes = null, attribution = null) { - const mergedAttributes = mergeAttrs(this._useAttributes, attributes) - const mergedAttribution = mergeAttrs(this._useAttribution, attribution) + const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) + const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) if (this._lastOp instanceof InsertOp && fun.equalityDeep(mergedAttributes, this._lastOp.attributes) && fun.equalityDeep(mergedAttribution, this._lastOp.attribution)) { this._lastOp.insert += insert } else { @@ -195,8 +195,8 @@ export class DeltaBuilder extends Delta { * @return {this} */ retain (retain, attributes = null, attribution = null) { - const mergedAttributes = mergeAttrs(this._useAttributes, attributes) - const mergedAttribution = mergeAttrs(this._useAttribution, attribution) + const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) + const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) if (this._lastOp instanceof RetainOp && fun.equalityDeep(mergedAttributes, this._lastOp.attributes) && fun.equalityDeep(mergedAttribution, this._lastOp.attribution)) { this._lastOp.retain += retain } else { diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 820f3fa10..d08e67949 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -121,6 +121,8 @@ export class AttrRange { export const createMaybeAttrRange = (clock, len, attrs) => new AttrRange(clock, len, /** @type {any} */ (attrs)) /** + * Whenever this is instantiated, it must receive a fresh array of ops, not something copied. + * * @template Attrs */ export class AttrRanges { diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index c92c248a8..bb2f34c4a 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2302,9 +2302,9 @@ export const testDeleteFormatting = _tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testAttributedContent = tc => { +export const testAttributedContent = _tc => { const ydoc = new Y.Doc({ gc: false }) const ytext = ydoc.getText() ytext.insert(0, 'Hello World!') @@ -2312,11 +2312,20 @@ export const testAttributedContent = tc => { ydoc.on('afterTransaction', tr => { am = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, [])) }) - ytext.applyDelta([{ retain: 6 }, { delete: 5 }, { insert: 'attributions' }]) - const attributedContent = ytext.getContent(am) - const expectedContent = delta.create().insert('Hello ').insert('World', {}, { changeType: 'delete' }).insert('attributions', {}, { changeType: 'insert' }).insert('!') - t.assert(attributedContent.equals(expectedContent)) - debugger + t.group('insert / delete / format', () => { + ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) + let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') + let attributedContent = ytext.getContent(am) + console.log(attributedContent.toJSON().ops) + t.assert(attributedContent.equals(expectedContent)) + }) + t.group('unformat', () => { + ytext.applyDelta([{retain: 5, attributes: { italic: null }}]) + let expectedContent = delta.create().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') + let attributedContent = ytext.getContent(am) + console.log(attributedContent.toJSON().ops) + t.assert(attributedContent.equals(expectedContent)) + }) } // RANDOM TESTS From 768d6b1cb23010f286bf7f72fa7359ed7c5f8324 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 22 Apr 2025 23:13:57 +0200 Subject: [PATCH 281/362] perf tests toDelta vs getContent --- tests/y-text.tests.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index bb2f34c4a..409a2b07b 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2308,21 +2308,23 @@ export const testAttributedContent = _tc => { const ydoc = new Y.Doc({ gc: false }) const ytext = ydoc.getText() ytext.insert(0, 'Hello World!') - let am = noAttributionsManager + let attributionManager = noAttributionsManager + ydoc.on('afterTransaction', tr => { - am = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, [])) + // attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')])) + attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, [])) }) t.group('insert / delete / format', () => { ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') - let attributedContent = ytext.getContent(am) + let attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON().ops) t.assert(attributedContent.equals(expectedContent)) }) t.group('unformat', () => { ytext.applyDelta([{retain: 5, attributes: { italic: null }}]) let expectedContent = delta.create().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') - let attributedContent = ytext.getContent(am) + let attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON().ops) t.assert(attributedContent.equals(expectedContent)) }) @@ -2553,7 +2555,14 @@ const checkResult = result => { * @param {any} d */ const typeToObject = d => d.insert instanceof Y.AbstractType ? d.insert.toJSON() : d - const p1 = result.users[i].getText('text').toDelta().map(typeToObject) + + t.measureTime('original toDelta perf', () => { + result.users[i-1].getText('text').toDelta().map(typeToObject) + }) + t.measureTime('getContent(attributionManager) performance)', () => { + result.users[i-1].getText('text').getContent() + }) + const p1 = result.users[i-1].getText('text').toDelta().map(typeToObject) const p2 = result.users[i].getText('text').toDelta().map(typeToObject) t.compare(p1, p2) } From 69786f7ee549cc4d7504f04ec7f6b6095738869b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Apr 2025 00:03:00 +0200 Subject: [PATCH 282/362] add another example and create a writeup for attributing content. --- attributing-content.md | 131 +++++++++++++++++++++++++++++++++++++++++ src/index.js | 6 +- src/utils/IdSet.js | 31 ++++++++++ tests/y-text.tests.js | 33 +++++++++++ 4 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 attributing-content.md diff --git a/attributing-content.md b/attributing-content.md new file mode 100644 index 000000000..78040133c --- /dev/null +++ b/attributing-content.md @@ -0,0 +1,131 @@ +# IdSets and IdMaps + +`IdSet` is a data structure (formerly `DeleteSet`) that allows us to efficiently +represent ranges of ids in Yjs (all content is identifyable by ids). + +`IdMap` is a new data structure that allows us to efficiently map ids to +attributes. It can be efficiently encoded. + +We can perform all usual set operations on `IdMap`s and `IdSet`s: diff, merge, +intersect. + +# Attribution of content + +In order to implement a Google Docs-like versioning feature, we want to be able +to attribute content with additional information (who created the change, +when was this change created, ..). + +When we click on a version in Google Docs, we might get annotated changes like +this: + +``` +# E.g. If Bob appends "world" to the previous version "hello " +[{ insert: 'hello' }, { insert: 'world', color: 'blue', creator: 'Bob', when: 'yesterday' }] +# E.g. If Bob deletes "world" from the previous version "hello world" +[{ insert: 'hello' }, { insert: 'world', backgroundColor: 'red', creator: 'Bob', when: 'yesterday' }] +``` + +In Yjs, we can now "attribute" changes with additional information. When we +render content using methods like `toString()` or `getDelta()`, Yjs will render +the unattributed content as-is, but it will render the attributed content with +the additional information. As all changes in Yjs are identifyable by Ids, we +can use `IdMap`s to map changes to "attributions". For example, we could +attribute deletions and insertions of a change and render them: + +```js +// We create some initial content "Hello World!". Then we create another +// document that will have a bunch of changes (make "Hell" italic, replace "World" +// with "Attribution"). +const ydocVersion0 = new Y.Doc({ gc: false }) +ydocVersion0.getText().insert(0, 'Hello World!') +const ydoc = new Y.Doc({ gc: false }) +Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocVersion0)) +const ytext = ydoc.getText() +ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) +// this represents to all insertions of ydoc +const insertionSet = Y.createInsertionSetFromStructStore(ydoc.store) +const deleteSet = Y.createDeleteSetFromStructStore(ydoc.store) +// exclude the changes from `ydocVersion0` +const insertionSetDiff = Y.diffIdSet(insertionSet, Y.createInsertionSetFromStructStore(ydocVersion0.store)) +const deleteSetDiff = Y.diffIdSet(deleteSet, Y.createDeleteSetFromStructStore(ydocVersion0.store)) +// assign attributes to the diff +const attributedInsertions = createIdMapFromIdSet(insertionSetDiff, [new Y.Attribution('insert', 'Bob')]) +const attributedDeletions = createIdMapFromIdSet(deleteSetDiff, [new Y.Attribution('delete', 'Bob')]) +// now we can define an attribution manager that maps these changes to output. One of the +// implementations is the TwosetAttributionManager +const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions) +// we render the attributed content with the attributionManager +let attributedContent = ytext.getContent(attributionManager) +console.log(JSON.stringify(attributedContent.toJSON().ops, null, 2)) +let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') +t.assert(attributedContent.equals(expectedContent)) + +// this is how the output would look like +const output = [ + { + "insert": "Hell", + "attributes": { + "italic": true + }, + "attribution": { + "attributes": { + "italic": [ + "Bob" + ] + } + } + }, + { + "insert": "o " + }, + { + "insert": "World", + "attribution": { + "delete": [ + "Bob" + ] + } + }, + { + "insert": "attributions", + "attribution": { + "insert": [ + "Bob" + ] + } + }, + { + "insert": "!" + } +] +``` + +We get a similar output to Google Docs: Insertions, Deletions, and changes to +formatting (attributes) are clearly associated to users. It will be the job of +the editor to render those changes with background-color etc.. + +Of course, we could associated changes also to multiple users like this: + +```js +const attributedDeletions = createIdMapFromIdSet(deleteSetDiff, [new Y.Attribution('insert', 'Bob'), new Y.Attribution('insert', 'OpenAI o3')]) +``` + +You could use the same output to calculate a real diff as well (consisting of +deletions and insertions only, without Attributions). + +`AttributionManager` is an abstract class for mapping attributions. It is +possible to highlight arbitrary content with this approach. + +The next steps are to: + +- finish the implementation for Y.Map and Y.Xml* (which should be easy, compared +to Y.Map). +- Implement an AttributionManager-CRDT for the backend that sits there and +associates changes with users. +- use `getContent(attributionManager)` instead of `toDelta` in y-prosemirror. +Would like to make the attribution part of y-prosemirror, however Nick can also +use this approach to customly render the changes in ProseMirror. + +The AttributionManager is encodes very efficiently. The ids are encoded using +run-length encoding and the Attributes are de-duplicated and only encoded once. +The above example encodes in 20 bytes. diff --git a/src/index.js b/src/index.js index 325092a2a..6aea1c521 100644 --- a/src/index.js +++ b/src/index.js @@ -105,7 +105,11 @@ export { IdMap, createIdMap, createAttribution, - Attribution + createInsertionSetFromStructStore, + diffIdMap, + diffIdSet, + Attribution, + encodeIdMap } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 39c26d566..4766cb907 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -357,6 +357,37 @@ export const createDeleteSetFromStructStore = ss => { return ds } +/** + * @param {import('../internals.js').StructStore} ss + */ +export const createInsertionSetFromStructStore = ss => { + const idset = createIdSet() + ss.clients.forEach((structs, client) => { + /** + * @type {Array} + */ + const iditems = [] + for (let i = 0; i < structs.length; i++) { + const struct = structs[i] + if (!struct.deleted) { + const clock = struct.id.clock + let len = struct.length + if (i + 1 < structs.length) { + for (let next = structs[i + 1]; i + 1 < structs.length && !next.deleted; next = structs[++i + 1]) { + len += next.length + } + } + iditems.push(new IdRange(clock, len)) + } + } + if (iditems.length > 0) { + idset.clients.set(client, new IdRanges(iditems)) + } + }) + return idset +} + + /** * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder * @param {IdSet} idSet diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 409a2b07b..f1b278f1b 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2330,6 +2330,38 @@ export const testAttributedContent = _tc => { }) } +/** + * @param {t.TestCase} _tc + */ +export const testAttributedDiffing = _tc => { + const ydocVersion0 = new Y.Doc({ gc: false }) + ydocVersion0.clientID = 0 + ydocVersion0.getText().insert(0, 'Hello World!') + const ydoc = new Y.Doc({ gc: false }) + ydoc.clientID = 1 + Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocVersion0)) + const ytext = ydoc.getText() + ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) + // this represents to all insertions of ydoc + const insertionSet = Y.createInsertionSetFromStructStore(ydoc.store) + const deleteSet = Y.createDeleteSetFromStructStore(ydoc.store) + // exclude the changes from `ydocVersion0` + const insertionSetDiff = Y.diffIdSet(insertionSet, Y.createInsertionSetFromStructStore(ydocVersion0.store)) + const deleteSetDiff = Y.diffIdSet(deleteSet, Y.createDeleteSetFromStructStore(ydocVersion0.store)) + // assign attributes to the diff + const attributedInsertions = createIdMapFromIdSet(insertionSetDiff, [new Y.Attribution('insert', 'Bob')]) + const attributedDeletions = createIdMapFromIdSet(deleteSetDiff, [new Y.Attribution('delete', 'Bob')]) + // now we can define an attribution manager that maps these changes to output. One of the + // implementations is the TwosetAttributionManager + const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions) + // we render the attributed content with the attributionManager + let attributedContent = ytext.getContent(attributionManager) + console.log(JSON.stringify(attributedContent.toJSON().ops, null, 2)) + let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') + t.assert(attributedContent.equals(expectedContent)) + console.log(Y.encodeIdMap(attributedInsertions).length) +} + // RANDOM TESTS let charCounter = 0 @@ -2556,6 +2588,7 @@ const checkResult = result => { */ const typeToObject = d => d.insert instanceof Y.AbstractType ? d.insert.toJSON() : d + t.info('length of text = ' + result.users[i-1].getText('text').length) t.measureTime('original toDelta perf', () => { result.users[i-1].getText('text').toDelta().map(typeToObject) }) From fbfe0e0eeb946fcc78524eb05c4c7042315c6028 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Apr 2025 17:46:00 +0200 Subject: [PATCH 283/362] working on performance --- src/utils/AttributionManager.js | 2 +- src/utils/Delta.js | 6 +++--- tests/y-text.tests.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index e3f42a1b1..a08ca6828 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -81,7 +81,7 @@ export class NoAttributionsManager { * @return {Array>} */ getContent (item) { - return item.deleted ? [] : [new AttributedContent(item.content, item.deleted, null)] + return item.deleted ? [] : [new AttributedContent(item.content, false, null)] } } diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 54712c194..e9a15f910 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -130,7 +130,7 @@ export class Delta { */ const mergeAttrs = (a, b) => { const merged = a == null ? b : (b == null ? a : object.assign({}, a, b)) - if (object.isEmpty(merged ?? {})) { return null } + if (merged == null || object.isEmpty(merged)) { return null } return merged } @@ -158,7 +158,7 @@ export class DeltaBuilder extends Delta { */ useAttributes (attributes) { if (this.usedAttributes === attributes) return this - this.usedAttributes = attributes && object.assign({}, attributes) + this.usedAttributes = attributes ?? object.assign({}, attributes) return this } @@ -167,7 +167,7 @@ export class DeltaBuilder extends Delta { */ useAttribution (attribution) { if (this.usedAttribution === attribution) return this - this.usedAttribution = attribution && object.assign({}, attribution) + this.usedAttribution = attribution ?? object.assign({}, attribution) return this } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index f1b278f1b..0b552756f 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2605,6 +2605,37 @@ const checkResult = result => { return result } +/** + * @param {t.TestCase} tc + */ +export const testAttributionManagerDefaultPerformance = tc => { + const N = 100000 + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + for (let i = 0; i < N; i++) { + if (prng.bool(tc.prng) && ytext.length > 0) { + const index = prng.int31(tc.prng, 0, ytext.length - 1) + const len = prng.int31(tc.prng, 0, math.min(ytext.length - index, 5)) + ytext.delete(index, len) + } else { + const index = prng.int31(tc.prng, 0, ytext.length) + const content = prng.utf16String(tc.prng, 30) + ytext.insert(index, content) + } + } + const M = 100 + t.measureTime('original toDelta perf', () => { + for (let i = 0; i < M; i++) { + ytext.toDelta() + } + }) + t.measureTime('getContent(attributionManager) performance)', () => { + for (let i = 0; i < M; i++) { + ytext.getContent() + } + }) +} + /** * @param {t.TestCase} tc */ From a6b2dd1d66e080564a704f628c4c4137f8b91c83 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Apr 2025 18:59:40 +0200 Subject: [PATCH 284/362] more perf improvements on getContent with attributedContent --- src/utils/Delta.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/Delta.js b/src/utils/Delta.js index e9a15f910..402550b7a 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -178,9 +178,9 @@ export class DeltaBuilder extends Delta { * @return {this} */ insert (insert, attributes = null, attribution = null) { - const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) - const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) - if (this._lastOp instanceof InsertOp && fun.equalityDeep(mergedAttributes, this._lastOp.attributes) && fun.equalityDeep(mergedAttribution, this._lastOp.attribution)) { + const mergedAttributes = attributes == null ? this.usedAttributes : mergeAttrs(this.usedAttributes, attributes) + const mergedAttribution = attribution == null ? this.usedAttribution : mergeAttrs(this.usedAttribution, attribution) + if (this._lastOp instanceof InsertOp && (mergedAttributes === this._lastOp.attributes || fun.equalityDeep(mergedAttributes, this._lastOp.attributes)) && (mergedAttribution === this._lastOp.attribution || fun.equalityDeep(mergedAttribution, this._lastOp.attribution))) { this._lastOp.insert += insert } else { this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) From f5e2a4d4f4e8d7b9b1e460f667b2c84d6d691f03 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Apr 2025 18:59:59 +0200 Subject: [PATCH 285/362] add test case for finding a type in another ydoc --- tests/doc.tests.js | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/doc.tests.js b/tests/doc.tests.js index bb94819d1..b55ee8617 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -19,6 +19,56 @@ export const testAfterTransactionRecursion = _tc => { }, 'test') } + +/** + * @param {t.TestCase} _tc + */ +export const testFindTypeInOtherDoc = _tc => { + const ydoc = new Y.Doc() + const ymap = ydoc.getMap() + const ytext = ymap.set('ytext', new Y.Text()) + const ydocClone = new Y.Doc() + Y.applyUpdate(ydocClone, Y.encodeStateAsUpdate(ydoc)) + /** + * @template {Y.AbstractType} Type + * @param {Type} ytype + * @param {Y.Doc} otherYdoc + * @return {Type} + */ + const findTypeInOtherYdoc = (ytype, otherYdoc) => { + const ydoc = /** @type {Y.Doc} */ (ytype.doc) + if (ytype._item === null) { + /** + * If is a root type, we need to find the root key in the original ydoc + * and use it to get the type in the other ydoc. + */ + const rootKey = Array.from(ydoc.share.keys()).find( + (key) => ydoc.share.get(key) === ytype + ) + if (rootKey == null) { + throw new Error('type does not exist in other ydoc') + } + return /** @type {Type} */ (otherYdoc.get(rootKey, /** @type {typeof Y.AbstractType} */ (ytype.constructor))) + } else { + /** + * If it is a sub type, we use the item id to find the history type. + */ + const ytypeItem = ytype._item + const otherStructs = otherYdoc.store.clients.get(ytypeItem.id.client) ?? [] + const itemIndex = Y.findIndexSS( + otherStructs, + ytypeItem.id.clock + ) + const otherItem = /** @type {Y.Item} */ (otherStructs[itemIndex]) + const otherContent = /** @type {Y.ContentType} */ (otherItem.content) + return /** @type {Type} */ (otherContent.type) + } + } + t.assert(findTypeInOtherYdoc(ymap, ydocClone) != null) + t.assert(findTypeInOtherYdoc(ytext, ydocClone) != null) +} + + /** * @param {t.TestCase} _tc */ From d399756eec3f975b7b93cb2de69ef8fdd1e57949 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 24 Apr 2025 19:49:08 +0200 Subject: [PATCH 286/362] more work on optimizing. Improve the test case. --- tests/y-text.tests.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 0b552756f..53da8631d 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2606,30 +2606,44 @@ const checkResult = result => { } /** + * Generally, the content reader with attributed content works better if there is more deleted + * content. Increasing the number of deletions improves performance of getContent (even to the point + * that is beats toString(), but that might be because of internal js optimization steps). + * * @param {t.TestCase} tc */ export const testAttributionManagerDefaultPerformance = tc => { const N = 100000 + const MaxDeletionLength = 5 // 25% chance of deletion + const MaxInsertionLength = 5 const ydoc = new Y.Doc() const ytext = ydoc.getText() for (let i = 0; i < N; i++) { - if (prng.bool(tc.prng) && ytext.length > 0) { + if (prng.bool(tc.prng) && prng.bool(tc.prng) && ytext.length > 0) { const index = prng.int31(tc.prng, 0, ytext.length - 1) - const len = prng.int31(tc.prng, 0, math.min(ytext.length - index, 5)) + const len = prng.int31(tc.prng, 0, math.min(ytext.length - index, MaxDeletionLength)) ytext.delete(index, len) } else { const index = prng.int31(tc.prng, 0, ytext.length) - const content = prng.utf16String(tc.prng, 30) + const content = prng.utf16String(tc.prng, MaxInsertionLength) ytext.insert(index, content) } } + t.info(`number of changes: ${N/1000}k`) + t.info(`length of text: ${ytext.length}`) const M = 100 - t.measureTime('original toDelta perf', () => { + + t.measureTime(`original toString perf `, () => { + for (let i = 0; i < M; i++) { + ytext.toDelta() + } + }) + t.measureTime(`original toDelta perf `, () => { for (let i = 0; i < M; i++) { ytext.toDelta() } }) - t.measureTime('getContent(attributionManager) performance)', () => { + t.measureTime(`getContent(attributionManager) performance `, () => { for (let i = 0; i < M; i++) { ytext.getContent() } From 324620d578205100fd556e10ce6573287d0211a9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 27 Apr 2025 22:50:48 +0200 Subject: [PATCH 287/362] [attribution manager] returns null instead of empty array to reduce gc allocations --- src/types/YText.js | 144 ++++++++++++++++---------------- src/utils/AttributionManager.js | 6 +- 2 files changed, 76 insertions(+), 74 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 74554cea6..da88b4028 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1008,82 +1008,84 @@ export class YText extends AbstractType { const d = delta.create() for (let item = this._start; item !== null; item = item.right) { const cs = am.getContent(item) - for (let i = 0; i < cs.length; i++) { - const { content, deleted, attrs } = cs[i] - /** - * @type {import('../utils/Delta.js').Attribution?} - */ - let attributions = null - if (attrs != null) { - attributions = {} - if (deleted) { - attributions.delete = [] - } else { - attributions.insert = [] - } - attrs.forEach(attr => { - switch (attr.name) { - case 'insert': - case 'delete': - case 'suggest': { - const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions) - const ls = as[attr.name] = as[attr.name] ?? [] - ls.push(attr.val) - break - } - default: { - if (attr.name[0] !== '_') { - /** @type {any} */ (attributions)[attr.name] = attr.val + if (cs != null) { + for (let i = 0; i < cs.length; i++) { + const { content, deleted, attrs } = cs[i] + /** + * @type {import('../utils/Delta.js').Attribution?} + */ + let attributions = null + if (attrs != null) { + attributions = {} + if (deleted) { + attributions.delete = [] + } else { + attributions.insert = [] + } + attrs.forEach(attr => { + switch (attr.name) { + case 'insert': + case 'delete': + case 'suggest': { + const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions) + const ls = as[attr.name] = as[attr.name] ?? [] + ls.push(attr.val) + break + } + default: { + if (attr.name[0] !== '_') { + /** @type {any} */ (attributions)[attr.name] = attr.val + } } } - } - }) - } - switch (content.constructor) { - case ContentString: { - d.insert(/** @type {ContentString} */ (content).str, null, attributions) - break + }) } - case ContentType: - case ContentEmbed: { - d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions) - break - } - case ContentFormat: - const contentFormat = /** @type {ContentFormat} */ (content) - if (attributions != null) { - /** - * @type {import('../utils/Delta.js').Attribution} - */ - const formattingAttributions = object.assign({}, d.usedAttribution) - const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {})) - if (contentFormat.value === null) { - delete attributesChanged[contentFormat.key] - } else { - const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] - by.push(...((deleted ? attributions.delete : attributions.insert) ?? [])) - const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) - if (attributedAt) formattingAttributions.attributedAt = attributedAt - } - if (object.isEmpty(attributesChanged)) { - d.useAttribution(null) - } else { - const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) - if (attributedAt != null) formattingAttributions.attributedAt = attributedAt - d.useAttribution(formattingAttributions) - } + switch (content.constructor) { + case ContentString: { + d.insert(/** @type {ContentString} */ (content).str, null, attributions) + break } - if (!deleted) { - const currAttrs = d.usedAttributes - if (contentFormat.value == null) { - const nextAttrs = object.assign({}, currAttrs) - delete nextAttrs[contentFormat.key] - d.useAttributes(nextAttrs) - } else { - d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) - } + case ContentType: + case ContentEmbed: { + d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions) + break } - break + case ContentFormat: + const contentFormat = /** @type {ContentFormat} */ (content) + if (attributions != null) { + /** + * @type {import('../utils/Delta.js').Attribution} + */ + const formattingAttributions = object.assign({}, d.usedAttribution) + const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {})) + if (contentFormat.value === null) { + delete attributesChanged[contentFormat.key] + } else { + const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] + by.push(...((deleted ? attributions.delete : attributions.insert) ?? [])) + const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) + if (attributedAt) formattingAttributions.attributedAt = attributedAt + } + if (object.isEmpty(attributesChanged)) { + d.useAttribution(null) + } else { + const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) + if (attributedAt != null) formattingAttributions.attributedAt = attributedAt + d.useAttribution(formattingAttributions) + } + } + if (!deleted) { + const currAttrs = d.usedAttributes + if (contentFormat.value == null) { + const nextAttrs = object.assign({}, currAttrs) + delete nextAttrs[contentFormat.key] + d.useAttributes(nextAttrs) + } else { + d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) + } + } + break + } } } } diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index a08ca6828..b2d455614 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -26,7 +26,7 @@ export class AttributedContent { export class AbstractAttributionManager { /** * @param {Item} _item - * @return {Array>} + * @return {Array>?} */ getContent (_item) { error.methodUnimplemented() @@ -78,10 +78,10 @@ export class TwosetAttributionManager { export class NoAttributionsManager { /** * @param {Item} item - * @return {Array>} + * @return {Array>?} */ getContent (item) { - return item.deleted ? [] : [new AttributedContent(item.content, false, null)] + return item.deleted ? null : [new AttributedContent(item.content, false, null)] } } From ece74661236d7b9d6a0de0e6a3d844fdcc6be0c7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 28 Apr 2025 00:02:06 +0200 Subject: [PATCH 288/362] [AttributionManager] further improve performance to be almost on-par with toString --- src/types/YText.js | 154 +++++++++++++++++--------------- src/utils/AttributionManager.js | 26 +++--- src/utils/Delta.js | 4 +- 3 files changed, 95 insertions(+), 89 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index da88b4028..7bd386d1f 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1006,85 +1006,91 @@ export class YText extends AbstractType { getContent (am = noAttributionsManager) { this.doc ?? warnPrematureAccess() const d = delta.create() - for (let item = this._start; item !== null; item = item.right) { - const cs = am.getContent(item) - if (cs != null) { - for (let i = 0; i < cs.length; i++) { - const { content, deleted, attrs } = cs[i] - /** - * @type {import('../utils/Delta.js').Attribution?} - */ - let attributions = null - if (attrs != null) { - attributions = {} - if (deleted) { - attributions.delete = [] - } else { - attributions.insert = [] - } - attrs.forEach(attr => { - switch (attr.name) { - case 'insert': - case 'delete': - case 'suggest': { - const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions) - const ls = as[attr.name] = as[attr.name] ?? [] - ls.push(attr.val) - break - } - default: { - if (attr.name[0] !== '_') { - /** @type {any} */ (attributions)[attr.name] = attr.val - } + /** + * @type {Array>} + */ + const cs = [] + for (let item = this._start; item !== null; cs.length = 0) { + // populate cs + for (; item !== null && cs.length < 50; item = item.right) { + am.readContent(cs, item) + } + for (let i = 0; i < cs.length; i++) { + const { content, deleted, attrs } = cs[i] + /** + * @type {import('../utils/Delta.js').Attribution?} + */ + let attributions = null + if (attrs != null) { + attributions = {} + if (deleted) { + attributions.delete = [] + } else { + attributions.insert = [] + } + attrs.forEach(attr => { + switch (attr.name) { + case 'insert': + case 'delete': + case 'suggest': { + const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions) + const ls = as[attr.name] = as[attr.name] ?? [] + ls.push(attr.val) + break + } + default: { + if (attr.name[0] !== '_') { + /** @type {any} */ (attributions)[attr.name] = attr.val } } - }) - } - switch (content.constructor) { - case ContentString: { - d.insert(/** @type {ContentString} */ (content).str, null, attributions) - break } - case ContentType: - case ContentEmbed: { - d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions) - break - } - case ContentFormat: - const contentFormat = /** @type {ContentFormat} */ (content) - if (attributions != null) { - /** - * @type {import('../utils/Delta.js').Attribution} - */ - const formattingAttributions = object.assign({}, d.usedAttribution) - const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {})) - if (contentFormat.value === null) { - delete attributesChanged[contentFormat.key] - } else { - const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] - by.push(...((deleted ? attributions.delete : attributions.insert) ?? [])) - const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) - if (attributedAt) formattingAttributions.attributedAt = attributedAt - } - if (object.isEmpty(attributesChanged)) { - d.useAttribution(null) - } else { - const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) - if (attributedAt != null) formattingAttributions.attributedAt = attributedAt - d.useAttribution(formattingAttributions) - } + }) + } + switch (content.constructor) { + case ContentString: { + d.insert(/** @type {ContentString} */ (content).str, null, attributions) + break + } + case ContentType: + case ContentEmbed: { + d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions) + break + } + case ContentFormat: { + const contentFormat = /** @type {ContentFormat} */ (content) + if (attributions != null) { + /** + * @type {import('../utils/Delta.js').Attribution} + */ + const formattingAttributions = object.assign({}, d.usedAttribution) + const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {})) + if (contentFormat.value === null) { + delete attributesChanged[contentFormat.key] + } else { + const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] + by.push(...((deleted ? attributions.delete : attributions.insert) ?? [])) + const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) + if (attributedAt) formattingAttributions.attributedAt = attributedAt } - if (!deleted) { - const currAttrs = d.usedAttributes - if (contentFormat.value == null) { - const nextAttrs = object.assign({}, currAttrs) - delete nextAttrs[contentFormat.key] - d.useAttributes(nextAttrs) - } else { - d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) - } + if (object.isEmpty(attributesChanged)) { + d.useAttribution(null) + } else { + const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) + if (attributedAt != null) formattingAttributions.attributedAt = attributedAt + d.useAttribution(formattingAttributions) } - break + } + if (!deleted) { + const currAttrs = d.usedAttributes + if (contentFormat.value == null) { + const nextAttrs = object.assign({}, currAttrs) + delete nextAttrs[contentFormat.key] + d.useAttributes(nextAttrs) + } else { + d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) + } + } + break } } } diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index b2d455614..95bfad772 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -25,10 +25,10 @@ export class AttributedContent { */ export class AbstractAttributionManager { /** + * @param {Array>} _contents * @param {Item} _item - * @return {Array>?} */ - getContent (_item) { + readContent (_contents, _item) { error.methodUnimplemented() } } @@ -49,24 +49,22 @@ export class TwosetAttributionManager { } /** + * @param {Array>} contents * @param {Item} item - * @return {Array>} */ - getContent (item) { + readContent (contents, item) { const deleted = item.deleted const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() - let res = slice.map(s => { + slice.forEach(s => { const c = content if (s.len < c.getLength()) { content = c.splice(s.len) } - return new AttributedContent(c, deleted, s.attrs) + if (!deleted || s.attrs != null) { + contents.push(new AttributedContent(c, deleted, s.attrs)) + } }) - if (deleted) { - res = res.filter(s => s.attrs != null) - } - return res } } @@ -77,11 +75,13 @@ export class TwosetAttributionManager { */ export class NoAttributionsManager { /** + * @param {Array>} contents * @param {Item} item - * @return {Array>?} */ - getContent (item) { - return item.deleted ? null : [new AttributedContent(item.content, false, null)] + readContent (contents, item) { + if (!item.deleted) { + contents.push(new AttributedContent(item.content, false, null)) + } } } diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 402550b7a..0bd701157 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -158,7 +158,7 @@ export class DeltaBuilder extends Delta { */ useAttributes (attributes) { if (this.usedAttributes === attributes) return this - this.usedAttributes = attributes ?? object.assign({}, attributes) + this.usedAttributes = attributes && (object.isEmpty(attributes) ? null : object.assign({}, attributes)) return this } @@ -167,7 +167,7 @@ export class DeltaBuilder extends Delta { */ useAttribution (attribution) { if (this.usedAttribution === attribution) return this - this.usedAttribution = attribution ?? object.assign({}, attribution) + this.usedAttribution = attribution && (object.isEmpty(attribution) ? null : object.assign({}, attribution)) return this } From b3171c535f42b72cdd63e8a3418cb897f18bbfb6 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 28 Apr 2025 02:42:06 +0200 Subject: [PATCH 289/362] getContent on Y.Map --- src/index.js | 2 +- src/types/YMap.js | 64 ++++++++++++++++++++++++++++++++- src/types/YText.js | 55 ++++++++-------------------- src/utils/AttributionManager.js | 52 ++++++++++++++++++++++++++- src/utils/Delta.js | 12 ++----- src/utils/IdMap.js | 49 ++++++++++++------------- src/utils/IdSet.js | 29 +++++++++++++++ tests/y-map.tests.js | 41 ++++++++++++++++++++- tests/y-text.tests.js | 1 - 9 files changed, 225 insertions(+), 80 deletions(-) diff --git a/src/index.js b/src/index.js index 6aea1c521..67d63c3de 100644 --- a/src/index.js +++ b/src/index.js @@ -108,7 +108,7 @@ export { createInsertionSetFromStructStore, diffIdMap, diffIdSet, - Attribution, + AttributionItem as Attribution, encodeIdMap } from './internals.js' diff --git a/src/types/YMap.js b/src/types/YMap.js index 22b94afb7..369d9408a 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -14,11 +14,18 @@ import { callTypeObservers, transact, warnPrematureAccess, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line + createAttributionFromAttrs } from '../internals.js' +import * as array from 'lib0/array' import * as iterator from 'lib0/iterator' +/** + * @template MapType + * @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent + */ + /** * @template T * @extends YEvent> @@ -186,6 +193,61 @@ export class YMap extends AbstractType { }) } + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {import('../internals.js').AbstractAttributionManager} am + * @return {MapAttributedContent} The Delta representation of this type. + * + * @public + */ + getContent (am) { + /** + * @type {MapAttributedContent} + */ + const mapcontent = {} + this._map.forEach((item, key) => { + /** + * @type {Array>} + */ + const cs = [] + am.readContent(cs, item) + const { deleted, attrs, content } = cs[cs.length - 1] + const c = array.last(content.getContent()) + const attribution = createAttributionFromAttrs(attrs, deleted) + if (deleted) { + mapcontent[key] = { prevValue: c, value: undefined, attribution } + } else { + /** + * @type {Array>} + */ + let cs = [] + for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) { + /** + * @type {Array>} + */ + const tmpcs = [] + am.readContent(tmpcs, prevItem) + cs = tmpcs.concat(cs) + if (cs[0].attrs == null) { + cs.splice(0, cs.findIndex(c => c.attrs != null)) + break + } + if (cs.length > 0) { + cs.length = 1 + } + } + const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined + mapcontent[key] = { prevValue, value: c, attribution } + } + }) + return mapcontent + } + /** * Returns an Iterator of [key, value] pairs * diff --git a/src/types/YText.js b/src/types/YText.js index 7bd386d1f..1f86b5005 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -26,7 +26,8 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction // eslint-disable-line + noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, // eslint-disable-line + createAttributionFromAttrs } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -1017,67 +1018,39 @@ export class YText extends AbstractType { } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] - /** - * @type {import('../utils/Delta.js').Attribution?} - */ - let attributions = null - if (attrs != null) { - attributions = {} - if (deleted) { - attributions.delete = [] - } else { - attributions.insert = [] - } - attrs.forEach(attr => { - switch (attr.name) { - case 'insert': - case 'delete': - case 'suggest': { - const as = /** @type {import('../utils/Delta.js').Attribution} */ (attributions) - const ls = as[attr.name] = as[attr.name] ?? [] - ls.push(attr.val) - break - } - default: { - if (attr.name[0] !== '_') { - /** @type {any} */ (attributions)[attr.name] = attr.val - } - } - } - }) - } + const attribution = createAttributionFromAttrs(attrs, deleted) switch (content.constructor) { case ContentString: { - d.insert(/** @type {ContentString} */ (content).str, null, attributions) + d.insert(/** @type {ContentString} */ (content).str, null, attribution) break } case ContentType: case ContentEmbed: { - d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attributions) + d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution) break } case ContentFormat: { const contentFormat = /** @type {ContentFormat} */ (content) - if (attributions != null) { + if (attribution != null) { /** * @type {import('../utils/Delta.js').Attribution} */ - const formattingAttributions = object.assign({}, d.usedAttribution) - const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttributions.attributes = object.assign({}, formattingAttributions.attributes ?? {})) + const formattingAttribution = object.assign({}, d.usedAttribution) + const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) if (contentFormat.value === null) { delete attributesChanged[contentFormat.key] } else { const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] - by.push(...((deleted ? attributions.delete : attributions.insert) ?? [])) - const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) - if (attributedAt) formattingAttributions.attributedAt = attributedAt + by.push(...((deleted ? attribution.delete : attribution.insert) ?? [])) + const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt) formattingAttribution.attributedAt = attributedAt } if (object.isEmpty(attributesChanged)) { d.useAttribution(null) } else { - const attributedAt = (deleted ? attributions.deletedAt : attributions.insertedAt) - if (attributedAt != null) formattingAttributions.attributedAt = attributedAt - d.useAttribution(formattingAttributions) + const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + d.useAttribution(formattingAttribution) } } if (!deleted) { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 95bfad772..7ae17eb5f 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -4,6 +4,56 @@ import { import * as error from 'lib0/error' +/** + * @typedef {Object} Attribution + * @property {Array} [Attribution.insert] + * @property {number} [Attribution.insertedAt] + * @property {Array} [Attribution.suggest] + * @property {number} [Attribution.suggestedAt] + * @property {Array} [Attribution.delete] + * @property {number} [Attribution.deletedAt] + * @property {{ [key: string]: Array }} [Attribution.attributes] + * @property {number} [Attribution.attributedAt] + */ + +/** + * @param {Array>?} attrs + * @param {boolean} deleted - whether the attributed item is deleted + * @return {Attribution?} + */ +export const createAttributionFromAttrs = (attrs, deleted) => { + /** + * @type {Attribution?} + */ + let attribution = null + if (attrs != null) { + attribution = {} + if (deleted) { + attribution.delete = [] + } else { + attribution.insert = [] + } + attrs.forEach(attr => { + switch (attr.name) { + case 'insert': + case 'delete': + case 'suggest': { + const as = /** @type {import('../utils/Delta.js').Attribution} */ (attribution) + const ls = as[attr.name] = as[attr.name] ?? [] + ls.push(attr.val) + break + } + default: { + if (attr.name[0] !== '_') { + /** @type {any} */ (attribution)[attr.name] = attr.val + } + } + } + }) + } + return attribution +} + /** * @template T */ @@ -11,7 +61,7 @@ export class AttributedContent { /** * @param {AbstractContent} content * @param {boolean} deleted - * @param {Array> | null} attrs + * @param {Array> | null} attrs */ constructor (content, deleted, attrs) { this.content = content diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 0bd701157..668fe3b6c 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -6,19 +6,11 @@ import * as fun from 'lib0/function' */ /** - * @typedef {{ [key: string]: any }} FormattingAttributes + * @typedef {import('./AttributionManager.js').Attribution} Attribution */ /** - * @typedef {Object} Attribution - * @property {Array} [Attribution.insert] - * @property {number} [Attribution.insertedAt] - * @property {Array} [Attribution.suggest] - * @property {number} [Attribution.suggestedAt] - * @property {Array} [Attribution.delete] - * @property {number} [Attribution.deletedAt] - * @property {{ [key: string]: Array }} [Attribution.attributes] - * @property {number} [Attribution.attributedAt] + * @typedef {{ [key: string]: any }} FormattingAttributes */ export class InsertOp { diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index d08e67949..335657c07 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -1,6 +1,7 @@ import { _diffSet, findIndexInIdRanges, + findRangeStartInIdRanges, DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID // eslint-disable-line } from '../internals.js' @@ -14,7 +15,7 @@ import * as rabin from 'lib0/hash/rabin' /** * @template V */ -export class Attribution { +export class AttributionItem { /** * @param {string} name * @param {V} val @@ -33,7 +34,7 @@ export class Attribution { } /** - * @param {Attribution} attr + * @param {AttributionItem} attr */ const _hashAttribution = attr => { const encoder = encoding.createEncoder() @@ -46,9 +47,9 @@ const _hashAttribution = attr => { * @template V * @param {string} name * @param {V} val - * @return {Attribution} + * @return {AttributionItem} */ -export const createAttribution = (name, val) => new Attribution(name, val) +export const createAttribution = (name, val) => new AttributionItem(name, val) /** * @template T @@ -79,7 +80,7 @@ export class AttrRange { /** * @param {number} clock * @param {number} len - * @param {Array>} attrs + * @param {Array>} attrs */ constructor (clock, len, attrs) { /** @@ -107,7 +108,7 @@ export class AttrRange { /** * @template Attrs - * @typedef {{ clock: number, len: number, attrs: Array>? }} MaybeAttrRange + * @typedef {{ clock: number, len: number, attrs: Array>? }} MaybeAttrRange */ /** @@ -115,7 +116,7 @@ export class AttrRange { * * @param {number} clock * @param {number} len - * @param {Array>?} attrs + * @param {Array>?} attrs * @return {MaybeAttrRange} */ export const createMaybeAttrRange = (clock, len, attrs) => new AttrRange(clock, len, /** @type {any} */ (attrs)) @@ -140,7 +141,7 @@ export class AttrRanges { /** * @param {number} clock * @param {number} length - * @param {Array>} attrs + * @param {Array>} attrs */ add (clock, length, attrs) { if (length === 0) return @@ -241,7 +242,7 @@ export const mergeIdMaps = ams => { /** * Maps attribution to the attribution of the merged idmap. * - * @type {Map,Attribution>} + * @type {Map,AttributionItem>} */ const attrMapper = new Map() const merged = createIdMap() @@ -271,7 +272,7 @@ export const mergeIdMaps = ams => { /** * @param {IdSet} idset - * @param {Array>} attrs + * @param {Array>} attrs */ export const createIdMapFromIdSet = (idset, attrs) => { const idmap = createIdMap() @@ -279,7 +280,7 @@ export const createIdMapFromIdSet = (idset, attrs) => { attrs = _ensureAttrs(idmap, attrs) // filter out duplicates /** - * @type {Array>} + * @type {Array>} */ const checkedAttrs = [] attrs.forEach(attr => { @@ -305,11 +306,11 @@ export class IdMap { */ this.clients = new Map() /** - * @type {Map>} + * @type {Map>} */ this.attrsH = new Map() /** - * @type {Set>} + * @type {Set>} */ this.attrs = new Set() } @@ -344,7 +345,7 @@ export class IdMap { * @type {Array>} */ const ranges = dr.getIds() - let index = findIndexInIdRanges(ranges, id.clock) + let index = findRangeStartInIdRanges(ranges, id.clock) if (index !== null) { let prev = null while (index < ranges.length) { @@ -356,9 +357,9 @@ export class IdMap { r = new AttrRange(r.clock, id.clock + len - r.clock, r.attrs) } if (r.len <= 0) break - const prevEnd = prev != null ? prev.clock + prev.len : index - if (prevEnd < index) { - res.push(createMaybeAttrRange(prevEnd, index - prevEnd, null)) + const prevEnd = prev != null ? prev.clock + prev.len : id.clock + if (prevEnd < r.clock) { + res.push(createMaybeAttrRange(prevEnd, r.clock - prevEnd, null)) } prev = r res.push(r) @@ -382,7 +383,7 @@ export class IdMap { * @param {number} client * @param {number} clock * @param {number} len - * @param {Array>} attrs + * @param {Array>} attrs */ add (client, clock, len, attrs) { if (len === 0) return @@ -411,7 +412,7 @@ export const writeIdMap = (encoder, idmap) => { encoding.writeVarUint(encoder.restEncoder, idmap.clients.size) let lastWrittenClientId = 0 /** - * @type {Map, number>} + * @type {Map, number>} */ const visitedAttributions = map.create() /** @@ -482,7 +483,7 @@ export const readIdMap = decoder => { const idmap = new IdMap() const numClients = decoding.readVarUint(decoder.restDecoder) /** - * @type {Array>} + * @type {Array>} */ const visitedAttributions = [] /** @@ -503,7 +504,7 @@ export const readIdMap = decoder => { const rangeClock = decoder.readDsClock() const rangeLen = decoder.readDsLen() /** - * @type {Array>} + * @type {Array>} */ const attrs = [] const attrsLen = decoding.readVarUint(decoder.restDecoder) @@ -515,7 +516,7 @@ export const readIdMap = decoder => { if (attrNameId >= visitedAttrNames.length) { visitedAttrNames.push(decoding.readVarString(decoder.restDecoder)) } - visitedAttributions.push(new Attribution(visitedAttrNames[attrNameId], decoding.readAny(decoder.restDecoder))) + visitedAttributions.push(new AttributionItem(visitedAttrNames[attrNameId], decoding.readAny(decoder.restDecoder))) } attrs.push(visitedAttributions[attrId]) } @@ -539,8 +540,8 @@ export const decodeIdMap = data => readIdMap(new DSDecoderV2(decoding.createDeco /** * @template Attrs * @param {IdMap} idmap - * @param {Array>} attrs - * @return {Array>} + * @param {Array>} attrs + * @return {Array>} */ const _ensureAttrs = (idmap, attrs) => attrs.map(attr => idmap.attrs.has(attr) diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 4766cb907..1703f30d6 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -170,6 +170,35 @@ export const findIndexInIdRanges = (dis, clock) => { return null } +/** + * Find the first range that contains clock or comes after clock. + * + * @param {Array} dis + * @param {number} clock + * @return {number|null} + * + * @private + * @function + */ +export const findRangeStartInIdRanges = (dis, clock) => { + let left = 0 + let right = dis.length - 1 + while (left <= right) { + const midindex = math.floor((left + right) / 2) + const mid = dis[midindex] + const midclock = mid.clock + if (midclock <= clock) { + if (clock < midclock + mid.len) { + return midindex + } + left = midindex + 1 + } else { + right = midindex - 1 + } + } + return left < dis.length ? left : null +} + /** * @param {Array} idSets * @return {IdSet} A fresh IdSet diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index 23c3f3daa..fbd64cd44 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -1,7 +1,11 @@ import * as Y from '../src/index.js' import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line +import * as delta from '../src/utils/Delta.js' import { - compareIDs + compareIDs, + noAttributionsManager, + TwosetAttributionManager, + createIdMapFromIdSet } from '../src/internals.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' @@ -613,6 +617,41 @@ export const testYmapEventHasCorrectValueWhenSettingAPrimitiveFromOtherUser = tc compare(users) } +/** + * @param {t.TestCase} _tc + */ +export const testAttributedContent = _tc => { + const ydoc = new Y.Doc({ gc: false }) + const ymap = ydoc.getMap() + let attributionManager = noAttributionsManager + + ydoc.on('afterTransaction', tr => { + // attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')])) + attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, [])) + }) + t.group('initial value', () => { + ymap.set('test', 42) + let expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } } + let attributedContent = ymap.getContent(attributionManager) + console.log(attributedContent) + t.compare(expectedContent, attributedContent) + }) + t.group('overwrite value', () => { + ymap.set('test', 'fourtytwo') + let expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } } + let attributedContent = ymap.getContent(attributionManager) + console.log(attributedContent) + t.compare(expectedContent, attributedContent) + }) + t.group('delete value', () => { + ymap.delete('test') + let expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } } + let attributedContent = ymap.getContent(attributionManager) + console.log(attributedContent) + t.compare(expectedContent, attributedContent) + }) +} + /** * @type {Array} */ diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 53da8631d..ebde95f52 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2632,7 +2632,6 @@ export const testAttributionManagerDefaultPerformance = tc => { t.info(`number of changes: ${N/1000}k`) t.info(`length of text: ${ytext.length}`) const M = 100 - t.measureTime(`original toString perf `, () => { for (let i = 0; i < M; i++) { ytext.toDelta() From 4f840247a3ea680aaa645e5d3a413c23ed61f3d7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 28 Apr 2025 17:06:32 +0200 Subject: [PATCH 290/362] implement and test getContent on all types --- src/index.js | 5 +- src/types/AbstractType.js | 106 +++++++++++++++++++++++++++++++++++++- src/types/YArray.js | 21 +++++++- src/types/YMap.js | 51 ++---------------- src/types/YText.js | 4 +- src/types/YXmlElement.js | 21 +++++++- src/types/YXmlFragment.js | 15 +++++- src/utils/Delta.js | 61 ++++++++++++++++++---- tests/delta.tests.js | 19 ++++++- tests/y-array.tests.js | 56 ++++++++++++++------ tests/y-xml.tests.js | 61 ++++++++++++++++++++++ 11 files changed, 337 insertions(+), 83 deletions(-) diff --git a/src/index.js b/src/index.js index 67d63c3de..b7df91036 100644 --- a/src/index.js +++ b/src/index.js @@ -109,7 +109,10 @@ export { diffIdMap, diffIdSet, AttributionItem as Attribution, - encodeIdMap + encodeIdMap, + createIdMapFromIdSet, + TwosetAttributionManager, + noAttributionsManager } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index a83f3a58d..145c3b506 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -10,9 +10,12 @@ import { ContentAny, ContentBinary, getItemCleanStart, - ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line + ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, + createAttributionFromAttrs, // eslint-disable-line } from '../internals.js' +import * as delta from '../utils/Delta.js' +import * as array from 'lib0/array' import * as map from 'lib0/map' import * as iterator from 'lib0/iterator' import * as error from 'lib0/error' @@ -466,6 +469,42 @@ export const typeListToArray = type => { return cs } +/** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @template MapType + * @param {AbstractType} type + * @param {import('../internals.js').AbstractAttributionManager} am + * @return {delta.Delta>} The Delta representation of this type. + * + * @private + * @function + */ +export const typeListGetContent = (type, am) => { + type.doc ?? warnPrematureAccess() + const d = /** @type {delta.DeltaBuilder>} */ (delta.create()) + /** + * @type {Array>} + */ + const cs = [] + for (let item = type._start; item !== null; cs.length = 0) { + // populate cs + for (; item !== null && cs.length < 50; item = item.right) { + am.readContent(cs, item) + } + for (let i = 0; i < cs.length; i++) { + const { content, deleted, attrs } = cs[i] + const attribution = createAttributionFromAttrs(attrs, deleted) + d.insert(content.getContent(), null, attribution) + } + } + return d.done() +} + /** * @param {AbstractType} type * @param {Snapshot} snapshot @@ -913,6 +952,71 @@ export const typeMapGetAll = (parent) => { return res } +/** + * @template MapType + * @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent + */ + +/** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @template MapType + * @param {AbstractType} parent + * @param {import('../internals.js').AbstractAttributionManager} am + * @return {MapAttributedContent} The Delta representation of this type. + * + * @private + * @function + */ +export const typeMapGetContent = (parent, am) => { + /** + * @type {MapAttributedContent} + */ + const mapcontent = {} + parent.doc ?? warnPrematureAccess() + parent._map.forEach((item, key) => { + /** + * @type {Array>} + */ + const cs = [] + am.readContent(cs, item) + const { deleted, attrs, content } = cs[cs.length - 1] + const c = array.last(content.getContent()) + const attribution = createAttributionFromAttrs(attrs, deleted) + if (deleted) { + mapcontent[key] = { prevValue: c, value: undefined, attribution } + } else { + /** + * @type {Array>} + */ + let cs = [] + for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) { + /** + * @type {Array>} + */ + const tmpcs = [] + am.readContent(tmpcs, prevItem) + cs = tmpcs.concat(cs) + if (cs[0].attrs == null) { + cs.splice(0, cs.findIndex(c => c.attrs != null)) + break + } + if (cs.length > 0) { + cs.length = 1 + } + } + const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined + mapcontent[key] = { prevValue, value: c, attribution } + } + }) + return mapcontent +} + + /** * @param {AbstractType} parent * @param {string} key diff --git a/src/types/YArray.js b/src/types/YArray.js index 8fd5c215a..0fd94e37e 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -17,9 +17,10 @@ import { callTypeObservers, transact, warnPrematureAccess, - ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line + ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line + AbstractAttributionManager } from '../internals.js' -import { typeListSlice } from './AbstractType.js' +import { typeListGetContent, typeListSlice } from './AbstractType.js' /** * Event that describes the changes on a YArray @@ -207,6 +208,22 @@ export class YArray extends AbstractType { return typeListToArray(this) } + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {AbstractAttributionManager} am + * @return {import('../utils/Delta.js').Delta>} The Delta representation of this type. + * + * @public + */ + getContent (am) { + return typeListGetContent(this, am) + } + /** * Returns a portion of this YArray into a JavaScript Array selected * from start to end (end not included). diff --git a/src/types/YMap.js b/src/types/YMap.js index 369d9408a..78f90e637 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -15,17 +15,13 @@ import { transact, warnPrematureAccess, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line - createAttributionFromAttrs + createAttributionFromAttrs, + typeMapGetContent } from '../internals.js' import * as array from 'lib0/array' import * as iterator from 'lib0/iterator' -/** - * @template MapType - * @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent - */ - /** * @template T * @extends YEvent> @@ -201,51 +197,12 @@ export class YMap extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {import('../internals.js').AbstractAttributionManager} am - * @return {MapAttributedContent} The Delta representation of this type. + * @return {import('./AbstractType.js').MapAttributedContent} The Delta representation of this type. * * @public */ getContent (am) { - /** - * @type {MapAttributedContent} - */ - const mapcontent = {} - this._map.forEach((item, key) => { - /** - * @type {Array>} - */ - const cs = [] - am.readContent(cs, item) - const { deleted, attrs, content } = cs[cs.length - 1] - const c = array.last(content.getContent()) - const attribution = createAttributionFromAttrs(attrs, deleted) - if (deleted) { - mapcontent[key] = { prevValue: c, value: undefined, attribution } - } else { - /** - * @type {Array>} - */ - let cs = [] - for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) { - /** - * @type {Array>} - */ - const tmpcs = [] - am.readContent(tmpcs, prevItem) - cs = tmpcs.concat(cs) - if (cs[0].attrs == null) { - cs.splice(0, cs.findIndex(c => c.attrs != null)) - break - } - if (cs.length > 0) { - cs.length = 1 - } - } - const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined - mapcontent[key] = { prevValue, value: c, attribution } - } - }) - return mapcontent + return typeMapGetContent(this, am) } /** diff --git a/src/types/YText.js b/src/types/YText.js index 1f86b5005..e00d6cb48 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1000,13 +1000,13 @@ export class YText extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').Delta} The Delta representation of this type. + * @return {import('../utils/Delta.js').Delta} The Delta representation of this type. * * @public */ getContent (am = noAttributionsManager) { this.doc ?? warnPrematureAccess() - const d = delta.create() + const d = delta.createTextDelta() /** * @type {Array>} */ diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 48029f698..b3a228aa5 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -11,7 +11,9 @@ import { typeMapGetAllSnapshot, typeListForEach, YXmlElementRefID, - Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line + typeMapGetContent, + noAttributionsManager, + Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line } from '../internals.js' /** @@ -206,6 +208,23 @@ export class YXmlElement extends YXmlFragment { return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this)) } + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {import('../internals.js').AbstractAttributionManager} am + * + * @public + */ + getContent (am = noAttributionsManager) { + const attributes = typeMapGetContent(this, am) + const { children } = super.getContent(am) + return { children, attributes } + } + /** * Creates a Dom Element that mirrors this YXmlElement. * diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 544a18ceb..72adf50c8 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -18,7 +18,9 @@ import { typeListGet, typeListSlice, warnPrematureAccess, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook // eslint-disable-line + noAttributionsManager, + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, // eslint-disable-line + typeListGetContent } from '../internals.js' import * as error from 'lib0/error' @@ -377,6 +379,17 @@ export class YXmlFragment extends AbstractType { return typeListToArray(this) } + /** + * Calculate the attributed content using the attribution manager. + * + * @param {import('../internals.js').AbstractAttributionManager} am + * @return {{ children: import('../utils/Delta.js').Delta> }} + */ + getContent (am = noAttributionsManager) { + const children = typeListGetContent(this, am) + return { children } + } + /** * Appends content to this YArray. * diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 668fe3b6c..794b12514 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -2,7 +2,8 @@ import * as object from 'lib0/object' import * as fun from 'lib0/function' /** - * @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp + * @template {string|Array|{[key: string]: any}} Content + * @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp */ /** @@ -13,9 +14,12 @@ import * as fun from 'lib0/function' * @typedef {{ [key: string]: any }} FormattingAttributes */ +/** + * @template {string|Array|{[key: string]: any}} Content + */ export class InsertOp { /** - * @param {string} insert + * @param {Content} insert * @param {FormattingAttributes|null} attributes * @param {Attribution|null} attribution */ @@ -60,16 +64,19 @@ export class RetainOp { } } +/** + * @template {string|Array|{[key: string]: any}} Content + */ export class Delta { constructor () { /** - * @type {Array} + * @type {Array>} */ this.ops = [] } /** - * @param {Delta} d + * @param {Delta} d * @return {boolean} */ equals (d) { @@ -85,9 +92,9 @@ export class Delta { } case InsertOp: { if ( - !fun.equalityDeep(/** @type {InsertOp} */ (op).insert, /** @type {InsertOp} */ (dop).insert) - || !fun.equalityDeep(/** @type {InsertOp} */ (op).attributes, /** @type {InsertOp} */ (dop).attributes) - || !fun.equalityDeep(/** @type {InsertOp} */ (op).attribution, /** @type {InsertOp} */ (dop).attribution) + !fun.equalityDeep(/** @type {InsertOp} */ (op).insert, /** @type {InsertOp} */ (dop).insert) + || !fun.equalityDeep(/** @type {InsertOp} */ (op).attributes, /** @type {InsertOp} */ (dop).attributes) + || !fun.equalityDeep(/** @type {InsertOp} */ (op).attribution, /** @type {InsertOp} */ (dop).attribution) ) { return false } @@ -126,6 +133,10 @@ const mergeAttrs = (a, b) => { return merged } +/** + * @template {string|Array|{[key: string]: any}} Content + * @extends Delta + */ export class DeltaBuilder extends Delta { constructor () { super() @@ -139,7 +150,7 @@ export class DeltaBuilder extends Delta { this.usedAttribution = null /** * @private - * @type {DeltaOp?} + * @type {DeltaOp?} */ this._lastOp = null } @@ -164,7 +175,7 @@ export class DeltaBuilder extends Delta { } /** - * @param {string} insert + * @param {Content} insert * @param {FormattingAttributes?} attributes * @param {Attribution?} attribution * @return {this} @@ -173,7 +184,15 @@ export class DeltaBuilder extends Delta { const mergedAttributes = attributes == null ? this.usedAttributes : mergeAttrs(this.usedAttributes, attributes) const mergedAttribution = attribution == null ? this.usedAttribution : mergeAttrs(this.usedAttribution, attribution) if (this._lastOp instanceof InsertOp && (mergedAttributes === this._lastOp.attributes || fun.equalityDeep(mergedAttributes, this._lastOp.attributes)) && (mergedAttribution === this._lastOp.attribution || fun.equalityDeep(mergedAttribution, this._lastOp.attribution))) { - this._lastOp.insert += insert + if (insert.constructor === String) { + // @ts-ignore + this._lastOp.insert += insert + } else if (insert.constructor === Array && this._lastOp.insert.constructor === Array) { + // @ts-ignore + this._lastOp.insert.push(...insert) + } else { + this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) + } } else { this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) } @@ -211,7 +230,7 @@ export class DeltaBuilder extends Delta { } /** - * @return {Delta} + * @return {Delta} */ done () { return this @@ -219,3 +238,23 @@ export class DeltaBuilder extends Delta { } export const create = () => new DeltaBuilder() + +/** + * @typedef {string | { [key: string]: any }} TextDeltaContent + */ + +/** + * @template {TextDeltaContent} Content + * @return {DeltaBuilder} + */ +export const createTextDelta = () => new DeltaBuilder() + +/** + * @typedef {Array} ArrayDeltaContent + */ + +/** + * @template {ArrayDeltaContent} Content + * @return {DeltaBuilder} + */ +export const createArrayDelta = () => new DeltaBuilder() diff --git a/tests/delta.tests.js b/tests/delta.tests.js index eac5b6f12..0c43c3276 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -5,6 +5,21 @@ 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' } }]) + const d = delta.create().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ insert: ['tester'] }).insert('!').done() + t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }]) +} + +/** + * @param {t.TestCase} _tc + */ +export const testDeltaMerging = _tc => { + const d = delta.create() + .insert('hello') + .insert('world') + .insert(' ', { italic: true }) + .insert({}) + .insert([1]) + .insert([2]) + .done() + t.compare(d.toJSON().ops, [{ insert: 'helloworld' }, { insert: ' ', attributes: { italic: true } }, { insert: {} }, { insert: [1, 2] }]) } diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index cae7650c9..f508f03ce 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -4,13 +4,14 @@ import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' import * as env from 'lib0/environment' +import * as delta from '../src/utils/Delta.js' const isDevMode = env.getVariable('node_env') === 'development' /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testBasicUpdate = tc => { +export const testBasicUpdate = _tc => { const doc1 = new Y.Doc() const doc2 = new Y.Doc() doc1.getArray('array').insert(0, ['hi']) @@ -20,9 +21,9 @@ export const testBasicUpdate = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testFailsObjectManipulationInDevMode = tc => { +export const testFailsObjectManipulationInDevMode = _tc => { if (isDevMode) { t.info('running in dev mode') const doc = new Y.Doc() @@ -42,9 +43,9 @@ export const testFailsObjectManipulationInDevMode = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testSlice = tc => { +export const testSlice = _tc => { const doc1 = new Y.Doc() const arr = doc1.getArray('array') arr.insert(0, [1, 2, 3]) @@ -57,9 +58,9 @@ export const testSlice = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testArrayFrom = tc => { +export const testArrayFrom = _tc => { const doc1 = new Y.Doc() const db1 = doc1.getMap('root') const nestedArray1 = Y.Array.from([0, 1, 2]) @@ -70,9 +71,9 @@ export const testArrayFrom = tc => { /** * Debugging yjs#297 - a critical bug connected to the search-marker approach * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testLengthIssue = tc => { +export const testLengthIssue = _tc => { const doc1 = new Y.Doc() const arr = doc1.getArray('array') arr.push([0, 1, 2, 3]) @@ -99,9 +100,9 @@ export const testLengthIssue = tc => { /** * Debugging yjs#314 * - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testLengthIssue2 = tc => { +export const testLengthIssue2 = _tc => { const doc = new Y.Doc() const next = doc.getArray() doc.transact(() => { @@ -288,7 +289,7 @@ export const testNestedObserverEvents = tc => { * @type {Array} */ const vals = [] - array0.observe(e => { + array0.observe(() => { if (array0.length === 1) { // inserting, will call this observer again // we expect that this observer is called after this event handler finishedn @@ -491,9 +492,9 @@ export const testEventTargetIsSetCorrectlyOnRemote = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testIteratingArrayContainingTypes = tc => { +export const testIteratingArrayContainingTypes = _tc => { const y = new Y.Doc() const arr = y.getArray('arr') const numItems = 10 @@ -509,6 +510,31 @@ export const testIteratingArrayContainingTypes = tc => { y.destroy() } +/** + * @param {t.TestCase} _tc + */ +export const testAttributedContent = _tc => { + const ydoc = new Y.Doc({ gc: false }) + const yarray = ydoc.getArray() + yarray.insert(0, [1, 2]) + let attributionManager = Y.noAttributionsManager + + ydoc.on('afterTransaction', tr => { + // attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')])) + attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, [])) + }) + t.group('insert / delete', () => { + ydoc.transact(() => { + yarray.delete(0, 1) + yarray.insert(1, [42]) + }) + let expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) + let attributedContent = yarray.getContent(attributionManager) + console.log(attributedContent.toJSON().ops) + t.assert(attributedContent.equals(expectedContent)) + }) +} + let _uniqueNumber = 0 const getUniqueNumber = () => _uniqueNumber++ diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index e2743daef..faaf65c49 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -1,6 +1,7 @@ import * as Y from '../src/index.js' import { init, compare } from './testHelper.js' import * as t from 'lib0/testing' +import * as delta from '../src/utils/Delta.js' export const testCustomTypings = () => { const ydoc = new Y.Doc() @@ -220,3 +221,63 @@ export const testElement = _tc => { yxmlel.insert(0, [text1, text2]) t.compareArrays(yxmlel.toArray(), [text1, text2]) } + +/** + * @param {t.TestCase} _tc + */ +export const testFragmentAttributedContent = _tc => { + const ydoc = new Y.Doc({ gc: false }) + const yfragment = new Y.XmlFragment() + const elem1 = new Y.XmlText('hello') + const elem2 = new Y.XmlElement() + const elem3 = new Y.XmlText('world') + yfragment.insert(0, [elem1, elem2]) + ydoc.getArray().insert(0, [yfragment]) + let attributionManager = Y.noAttributionsManager + ydoc.on('afterTransaction', tr => { + // attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')])) + attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, [])) + }) + t.group('insert / delete', () => { + ydoc.transact(() => { + yfragment.delete(0, 1) + yfragment.insert(1, [elem3]) + }) + let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + let attributedContent = yfragment.getContent(attributionManager) + console.log(attributedContent.children.toJSON().ops) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) + }) +} + +/** + * @param {t.TestCase} _tc + */ +export const testElementAttributedContent = _tc => { + const ydoc = new Y.Doc({ gc: false }) + const yelement = ydoc.getXmlElement('p') + const elem1 = new Y.XmlElement('span') + const elem2 = new Y.XmlText('hello') + const elem3 = new Y.XmlText('world') + yelement.insert(0, [elem1, elem2]) + let attributionManager = Y.noAttributionsManager + ydoc.on('afterTransaction', tr => { + // attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')])) + attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, [])) + }) + t.group('insert / delete', () => { + ydoc.transact(() => { + yelement.delete(0, 1) + yelement.insert(1, [elem3]) + yelement.setAttribute('key', '42') + }) + let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + let attributedContent = yelement.getContent(attributionManager) + console.log('children', attributedContent.children.toJSON().ops) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + }) +} + From 1722c8a36f3fd295e58484304a0b1b2fe34ee4e2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 29 Apr 2025 18:02:15 +0200 Subject: [PATCH 291/362] Implement & test getContentDeep for all types. Improve ability to compare things using lib0/traits. --- package-lock.json | 8 +- package.json | 2 +- src/types/AbstractType.js | 33 +++++-- src/types/YArray.js | 33 +++++-- src/types/YMap.js | 6 +- src/types/YText.js | 24 ++++- src/types/YXmlElement.js | 34 ++++++- src/types/YXmlFragment.js | 17 +++- src/utils/Delta.js | 180 ++++++++++++++++++++++++++------------ src/utils/IdSet.js | 1 - test.html | 6 ++ tests/IdMap.tests.js | 2 +- tests/delta.tests.js | 4 +- tests/doc.tests.js | 4 +- tests/y-array.tests.js | 4 +- tests/y-map.tests.js | 13 ++- tests/y-text.tests.js | 24 ++--- tests/y-xml.tests.js | 30 +++++-- 18 files changed, 309 insertions(+), 116 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8fdb6e199..498cb9330 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "13.6.27", "license": "MIT", "dependencies": { - "lib0": "^0.2.104", + "lib0": "^0.2.105", "y-protocols": "^1.0.5" }, "devDependencies": { @@ -2774,9 +2774,9 @@ } }, "node_modules/lib0": { - "version": "0.2.104", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.104.tgz", - "integrity": "sha512-1tqKRANSPTcjs/yjPoKh52oRM2u5AYdd8jie8sDiN8/5kpWWiQSHUGgtB4VEXLw1chVL3QPSPp8q9RWqzSn2FA==", + "version": "0.2.105", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.105.tgz", + "integrity": "sha512-5vtbuBi2P43ZYOfVMV+TZYkWEa0J9kijXirzEgrPA+nJDQCtMx805/rqW4G1nXbM9IRIhwW+OyNNgcQdbhKfSw==", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" diff --git a/package.json b/package.json index 46686830a..da7b1ab19 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.104", + "lib0": "^0.2.105", "y-protocols": "^1.0.5" }, "devDependencies": { diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 145c3b506..2f1b6f7e5 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -10,8 +10,7 @@ import { ContentAny, ContentBinary, getItemCleanStart, - ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, - createAttributionFromAttrs, // eslint-disable-line + ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttrs, AbstractAttributionManager, // eslint-disable-line } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -22,6 +21,13 @@ import * as error from 'lib0/error' import * as math from 'lib0/math' import * as log from 'lib0/logging' +/** + * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution } } }} YXmlDeepContent + */ +/** + * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution} } }} DeepContent + */ + /** * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats */ @@ -406,6 +412,22 @@ export class AbstractType { * @return {any} */ toJSON () {} + + /** + * @param {AbstractAttributionManager} _am + * @return {any} + */ + getContent (_am) { + error.methodUnimplemented() + } + + /** + * @param {AbstractAttributionManager} _am + * @return {DeepContent} + */ + getContentDeep (_am) { + error.methodUnimplemented() + } } /** @@ -476,17 +498,15 @@ export const typeListToArray = type => { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @template MapType * @param {AbstractType} type * @param {import('../internals.js').AbstractAttributionManager} am - * @return {delta.Delta>} The Delta representation of this type. * * @private * @function */ export const typeListGetContent = (type, am) => { type.doc ?? warnPrematureAccess() - const d = /** @type {delta.DeltaBuilder>} */ (delta.create()) + const d = delta.createArrayDelta() /** * @type {Array>} */ @@ -502,7 +522,7 @@ export const typeListGetContent = (type, am) => { d.insert(content.getContent(), null, attribution) } } - return d.done() + return d } /** @@ -1016,7 +1036,6 @@ export const typeMapGetContent = (parent, am) => { return mapcontent } - /** * @param {AbstractType} parent * @param {string} key diff --git a/src/types/YArray.js b/src/types/YArray.js index 0fd94e37e..086ac0017 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -17,10 +17,13 @@ import { callTypeObservers, transact, warnPrematureAccess, - ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line - AbstractAttributionManager + typeListGetContent, + typeListSlice, + noAttributionsManager, + AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' -import { typeListGetContent, typeListSlice } from './AbstractType.js' + +import * as delta from '../utils/Delta.js' /** * Event that describes the changes on a YArray @@ -216,11 +219,31 @@ export class YArray extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').Delta>} The Delta representation of this type. + * @return {import('../utils/Delta.js').ArrayDelta>} The Delta representation of this type. + * + * @public + */ + getContentDeep (am = noAttributionsManager) { + return this.getContent(am).map(d => /** @type {any} */ ( + d instanceof delta.InsertOp && d.insert instanceof Array + ? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) + : d + )) + } + + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {AbstractAttributionManager} am + * @return {import('../utils/Delta.js').ArrayDelta>} The Delta representation of this type. * * @public */ - getContent (am) { + getContent (am = noAttributionsManager) { return typeListGetContent(this, am) } diff --git a/src/types/YMap.js b/src/types/YMap.js index 78f90e637..7c0e0cbef 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -13,13 +13,11 @@ import { YMapRefID, callTypeObservers, transact, + typeMapGetContent, warnPrematureAccess, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line - createAttributionFromAttrs, - typeMapGetContent + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' -import * as array from 'lib0/array' import * as iterator from 'lib0/iterator' /** diff --git a/src/types/YText.js b/src/types/YText.js index e00d6cb48..fa32cc801 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1000,7 +1000,27 @@ export class YText extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').Delta} The Delta representation of this type. + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. + * + * @public + */ + getContentDeep (am = noAttributionsManager) { + return this.getContent(am).map(d => + d instanceof delta.InsertOp && d.insert instanceof AbstractType + ? new delta.InsertOp(d.insert.getContent(am), d.attributes, d.attribution) + : d + ) + } + + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {AbstractAttributionManager} am + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * * @public */ @@ -1068,7 +1088,7 @@ export class YText extends AbstractType { } } } - return d.done() + return d } /** diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index b3a228aa5..aca5fbb05 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -13,9 +13,11 @@ import { YXmlElementRefID, typeMapGetContent, noAttributionsManager, - Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line + AbstractAttributionManager, Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line } from '../internals.js' +import * as delta from '../utils/Delta.js' + /** * @typedef {Object|number|null|Array|string|Uint8Array|AbstractType} ValueTypes */ @@ -208,6 +210,36 @@ export class YXmlElement extends YXmlFragment { return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this)) } + /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {AbstractAttributionManager} am + * @return {{ nodeName: string, children: delta.ArrayDelta>, attributes: import('./AbstractType.js').MapAttributedContent }} + * + * @public + */ + getContentDeep (am = noAttributionsManager) { + const { children: origChildren, attributes: origAttributes } = this.getContent(am) + const children = origChildren.map(d => /** @type {any} */ ( + (d instanceof delta.InsertOp && d.insert instanceof Array) + ? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) + : d + )) + /** + * @todo there is a Attributes type and a DeepAttributes type. + * @type {import('./AbstractType.js').MapAttributedContent} + */ + const attributes = {} + object.forEach(origAttributes, (v, key) => { + attributes[key] = Object.assign({}, v, { value: v.value instanceof AbstractType ? v.value.getContentDeep(am) : v.value }) + }) + return { nodeName: this.nodeName, children, attributes } + } + /** * Render the difference to another ydoc (which can be empty) and highlight the differences with * attributions. diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 72adf50c8..c60f11ce3 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -383,13 +383,28 @@ export class YXmlFragment extends AbstractType { * Calculate the attributed content using the attribution manager. * * @param {import('../internals.js').AbstractAttributionManager} am - * @return {{ children: import('../utils/Delta.js').Delta> }} + * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} */ getContent (am = noAttributionsManager) { const children = typeListGetContent(this, am) return { children } } + /** + * Calculate the attributed content using the attribution manager. + * + * @param {import('../internals.js').AbstractAttributionManager} am + * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} + */ + getContentDeep (am) { + const { children: origChildren } = this.getContent() + /** + * @type {import('../utils/Delta.js').ArrayDelta>} + */ + const children = origChildren.map(d => /** @type {any} */ (d instanceof AbstractType ? d.getContentDeep(am) : d)) + return { children } + } + /** * Appends content to this YArray. * diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 794b12514..22102614e 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -1,5 +1,6 @@ import * as object from 'lib0/object' import * as fun from 'lib0/function' +import * as traits from 'lib0/traits' /** * @template {string|Array|{[key: string]: any}} Content @@ -29,9 +30,20 @@ export class InsertOp { this.attribution = attribution } + get length () { + return (this.insert.constructor === Array || this.insert.constructor === String) ? this.insert.length : 1 + } + toJSON () { return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) } + + /** + * @param {InsertOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) + } } export class DeleteOp { @@ -42,9 +54,20 @@ export class DeleteOp { this.delete = len } + get length () { + return 0 + } + toJSON () { return { delete: this.delete } } + + /** + * @param {DeleteOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.delete === other.delete + } } export class RetainOp { @@ -59,16 +82,48 @@ export class RetainOp { this.attribution = attribution } + get length () { + return this.retain + } + toJSON () { return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {}) } + + /** + * @param {RetainOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.retain === other.retain && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) + } } /** - * @template {string|Array|{[key: string]: any}} Content + * @typedef {Array} ArrayDeltaContent */ -export class Delta { - constructor () { + +/** + * @typedef {string | { [key: string]: any }} TextDeltaContent + */ + +/** + * @typedef {{ array: ArrayDeltaContent, text: TextDeltaContent, custom: string|Array|{[key:string]:any}}} DeltaTypeMapper + */ + +/** + * @typedef {(TextDelta | ArrayDelta)} Delta + */ + +/** + * @template {'array' | 'text' | 'custom'} Type + * @template {DeltaTypeMapper[Type]} [Content=DeltaTypeMapper[Type]] + */ +export class AbstractDelta { + /** + * @param {Type} type + */ + constructor (type) { + this.type = type /** * @type {Array>} */ @@ -76,48 +131,49 @@ export class Delta { } /** - * @param {Delta} d + * @template {DeltaTypeMapper[Type]} MContent + * @param {(d:DeltaOp)=>DeltaOp} f + * @return {DeltaBuilder} + */ + map (f) { + const d = /** @type {DeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) + d.ops = this.ops.map(f) + // @ts-ignore + d._lastOp = d.ops[d.ops.length - 1] ?? null + return d + } + + /** + * @param {(d:DeltaOp,index:number)=>void} f + */ + forEach (f) { + for ( + let i = 0, index = 0, op = this.ops[i]; + i < this.ops.length; + i++, index += op.length, op = this.ops[i] + ) { + f(op, index) + } + } + + /** + * @param {AbstractDelta} other * @return {boolean} */ - equals (d) { - return this.ops.length === d.ops.length && this.ops.every((op, i) => { - const dop = d.ops[i] - if (op.constructor !== dop.constructor) return false - switch (op.constructor) { - case DeleteOp: { - if (/** @type {DeleteOp} */ (op).delete !== /** @type {DeleteOp} */ (dop).delete) { - return false - } - break - } - case InsertOp: { - if ( - !fun.equalityDeep(/** @type {InsertOp} */ (op).insert, /** @type {InsertOp} */ (dop).insert) - || !fun.equalityDeep(/** @type {InsertOp} */ (op).attributes, /** @type {InsertOp} */ (dop).attributes) - || !fun.equalityDeep(/** @type {InsertOp} */ (op).attribution, /** @type {InsertOp} */ (dop).attribution) - ) { - return false - } - break - } - case RetainOp: { - if ( - /** @type {RetainOp} */ (op).retain !== /** @type {RetainOp} */ (dop).retain - || !fun.equalityDeep(/** @type {RetainOp} */ (op).attributes, /** @type {RetainOp} */ (dop).attributes) - || !fun.equalityDeep(/** @type {RetainOp} */ (op).attribution, /** @type {RetainOp} */ (dop).attribution) - ) { - return false - } - break - } - } - return true - }) + equals (other) { + return this[traits.EqualityTraitSymbol](other) } toJSON () { return { ops: this.ops.map(o => o.toJSON()) } } + + /** + * @param {AbstractDelta} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.type === other.type && fun.equalityDeep(this.ops, other.ops) + } } /** @@ -134,12 +190,16 @@ const mergeAttrs = (a, b) => { } /** - * @template {string|Array|{[key: string]: any}} Content - * @extends Delta + * @template {'array' | 'text' | 'custom'} [Type='custom'] + * @template {DeltaTypeMapper[Type]} [Content=DeltaTypeMapper[Type]] + * @extends AbstractDelta */ -export class DeltaBuilder extends Delta { - constructor () { - super() +export class DeltaBuilder extends AbstractDelta { + /** + * @param {Type} type + */ + constructor (type) { + super(type) /** * @type {FormattingAttributes?} */ @@ -185,10 +245,10 @@ export class DeltaBuilder extends Delta { const mergedAttribution = attribution == null ? this.usedAttribution : mergeAttrs(this.usedAttribution, attribution) if (this._lastOp instanceof InsertOp && (mergedAttributes === this._lastOp.attributes || fun.equalityDeep(mergedAttributes, this._lastOp.attributes)) && (mergedAttribution === this._lastOp.attribution || fun.equalityDeep(mergedAttribution, this._lastOp.attribution))) { if (insert.constructor === String) { - // @ts-ignore + // @ts-ignore this._lastOp.insert += insert } else if (insert.constructor === Array && this._lastOp.insert.constructor === Array) { - // @ts-ignore + // @ts-ignore this._lastOp.insert.push(...insert) } else { this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) @@ -230,31 +290,39 @@ export class DeltaBuilder extends Delta { } /** - * @return {Delta} + * @return {AbstractDelta} */ done () { return this } } -export const create = () => new DeltaBuilder() - /** - * @typedef {string | { [key: string]: any }} TextDeltaContent + * @template {ArrayDeltaContent} [Content=ArrayDeltaContent] + * @extends DeltaBuilder<'array',Content> */ +export class ArrayDelta extends DeltaBuilder { + constructor () { + super('array') + } +} /** - * @template {TextDeltaContent} Content - * @return {DeltaBuilder} + * @template {TextDeltaContent} [Content=TextDeltaContent] + * @extends DeltaBuilder<'text',Content> */ -export const createTextDelta = () => new DeltaBuilder() +export class TextDelta extends DeltaBuilder { + constructor () { + super('text') + } +} /** - * @typedef {Array} ArrayDeltaContent + * @return {TextDelta} */ +export const createTextDelta = () => new TextDelta() /** - * @template {ArrayDeltaContent} Content - * @return {DeltaBuilder} + * @return {ArrayDelta} */ -export const createArrayDelta = () => new DeltaBuilder() +export const createArrayDelta = () => new ArrayDelta() diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 1703f30d6..82fda2999 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -416,7 +416,6 @@ export const createInsertionSetFromStructStore = ss => { return idset } - /** * @param {IdSetEncoderV1 | IdSetEncoderV2} encoder * @param {IdSet} idSet diff --git a/test.html b/test.html index 28bf9ad35..9273884e4 100644 --- a/test.html +++ b/test.html @@ -135,6 +135,9 @@ "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/traits.js": "./node_modules/lib0/traits.js", + "lib0/dist/traits.cjs": "./node_modules/lib0/dist/traits.cjs", + "lib0/traits": "./node_modules/lib0/traits.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", @@ -296,6 +299,9 @@ "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/traits.js": "./node_modules/lib0/traits.js", + "lib0/dist/traits.cjs": "./node_modules/lib0/dist/traits.cjs", + "lib0/traits": "./node_modules/lib0/traits.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", diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 79809e2e1..cf7ddae7a 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,6 +1,6 @@ import * as t from 'lib0/testing' import * as idmap from '../src/utils/IdMap.js' -import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution, validateIdMap } from './testHelper.js' +import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js' import * as YY from '../src/internals.js' /** diff --git a/tests/delta.tests.js b/tests/delta.tests.js index 0c43c3276..0237c974b 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -5,7 +5,7 @@ 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({ insert: ['tester'] }).insert('!').done() + const d = delta.createTextDelta().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ insert: ['tester'] }).insert('!').done() t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }]) } @@ -13,7 +13,7 @@ export const testDelta = _tc => { * @param {t.TestCase} _tc */ export const testDeltaMerging = _tc => { - const d = delta.create() + const d = delta.createTextDelta() .insert('hello') .insert('world') .insert(' ', { italic: true }) diff --git a/tests/doc.tests.js b/tests/doc.tests.js index b55ee8617..80db1b281 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -19,7 +19,6 @@ export const testAfterTransactionRecursion = _tc => { }, 'test') } - /** * @param {t.TestCase} _tc */ @@ -39,7 +38,7 @@ export const testFindTypeInOtherDoc = _tc => { const ydoc = /** @type {Y.Doc} */ (ytype.doc) if (ytype._item === null) { /** - * If is a root type, we need to find the root key in the original ydoc + * If is a root type, we need to find the root key in the original ydoc * and use it to get the type in the other ydoc. */ const rootKey = Array.from(ydoc.share.keys()).find( @@ -68,7 +67,6 @@ export const testFindTypeInOtherDoc = _tc => { t.assert(findTypeInOtherYdoc(ytext, ydocClone) != null) } - /** * @param {t.TestCase} _tc */ diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index f508f03ce..faad66c4c 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -528,8 +528,8 @@ export const testAttributedContent = _tc => { yarray.delete(0, 1) yarray.insert(1, [42]) }) - let expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) - let attributedContent = yarray.getContent(attributionManager) + const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) + const attributedContent = yarray.getContent(attributionManager) console.log(attributedContent.toJSON().ops) t.assert(attributedContent.equals(expectedContent)) }) diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index fbd64cd44..ff056e9ea 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -1,6 +1,5 @@ import * as Y from '../src/index.js' import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line -import * as delta from '../src/utils/Delta.js' import { compareIDs, noAttributionsManager, @@ -631,22 +630,22 @@ export const testAttributedContent = _tc => { }) t.group('initial value', () => { ymap.set('test', 42) - let expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } } - let attributedContent = ymap.getContent(attributionManager) + const expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } } + const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent) }) t.group('overwrite value', () => { ymap.set('test', 'fourtytwo') - let expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } } - let attributedContent = ymap.getContent(attributionManager) + const expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } } + const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent) }) t.group('delete value', () => { ymap.delete('test') - let expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } } - let attributedContent = ymap.getContent(attributionManager) + const expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } } + const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent) }) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index ebde95f52..95bb3837d 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2316,15 +2316,15 @@ export const testAttributedContent = _tc => { }) t.group('insert / delete / format', () => { ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) - let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') - let attributedContent = ytext.getContent(attributionManager) + const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') + const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON().ops) t.assert(attributedContent.equals(expectedContent)) }) t.group('unformat', () => { - ytext.applyDelta([{retain: 5, attributes: { italic: null }}]) - let expectedContent = delta.create().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') - let attributedContent = ytext.getContent(attributionManager) + ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) + const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') + const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON().ops) t.assert(attributedContent.equals(expectedContent)) }) @@ -2355,9 +2355,9 @@ export const testAttributedDiffing = _tc => { // implementations is the TwosetAttributionManager const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions) // we render the attributed content with the attributionManager - let attributedContent = ytext.getContent(attributionManager) + const attributedContent = ytext.getContent(attributionManager) console.log(JSON.stringify(attributedContent.toJSON().ops, null, 2)) - let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') + const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') t.assert(attributedContent.equals(expectedContent)) console.log(Y.encodeIdMap(attributedInsertions).length) } @@ -2588,14 +2588,14 @@ const checkResult = result => { */ const typeToObject = d => d.insert instanceof Y.AbstractType ? d.insert.toJSON() : d - t.info('length of text = ' + result.users[i-1].getText('text').length) + t.info('length of text = ' + result.users[i - 1].getText('text').length) t.measureTime('original toDelta perf', () => { - result.users[i-1].getText('text').toDelta().map(typeToObject) + result.users[i - 1].getText('text').toDelta().map(typeToObject) }) t.measureTime('getContent(attributionManager) performance)', () => { - result.users[i-1].getText('text').getContent() + result.users[i - 1].getText('text').getContent() }) - const p1 = result.users[i-1].getText('text').toDelta().map(typeToObject) + const p1 = result.users[i - 1].getText('text').toDelta().map(typeToObject) const p2 = result.users[i].getText('text').toDelta().map(typeToObject) t.compare(p1, p2) } @@ -2629,7 +2629,7 @@ export const testAttributionManagerDefaultPerformance = tc => { ytext.insert(index, content) } } - t.info(`number of changes: ${N/1000}k`) + t.info(`number of changes: ${N / 1000}k`) t.info(`length of text: ${ytext.length}`) const M = 100 t.measureTime(`original toString perf `, () => { diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index faaf65c49..72dd9dc4a 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -243,8 +243,8 @@ export const testFragmentAttributedContent = _tc => { yfragment.delete(0, 1) yfragment.insert(1, [elem3]) }) - let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) - let attributedContent = yfragment.getContent(attributionManager) + const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + const attributedContent = yfragment.getContent(attributionManager) console.log(attributedContent.children.toJSON().ops) t.assert(attributedContent.children.equals(expectedContent)) t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) @@ -257,8 +257,8 @@ export const testFragmentAttributedContent = _tc => { export const testElementAttributedContent = _tc => { const ydoc = new Y.Doc({ gc: false }) const yelement = ydoc.getXmlElement('p') - const elem1 = new Y.XmlElement('span') - const elem2 = new Y.XmlText('hello') + const elem1 = new Y.XmlText('hello') + const elem2 = new Y.XmlElement('span') const elem3 = new Y.XmlText('world') yelement.insert(0, [elem1, elem2]) let attributionManager = Y.noAttributionsManager @@ -272,12 +272,28 @@ export const testElementAttributedContent = _tc => { yelement.insert(1, [elem3]) yelement.setAttribute('key', '42') }) - let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) - let attributedContent = yelement.getContent(attributionManager) + const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + const attributedContent = yelement.getContent(attributionManager) console.log('children', attributedContent.children.toJSON().ops) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.group('test getContentDeep', () => { + const expectedContent = delta.createArrayDelta().insert( + [delta.createTextDelta().insert('hello', null, { delete: [] })], + null, + { delete: [] } + ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + .insert([ + delta.createTextDelta().insert('world', null, { insert: [] }) + ], null, { insert: [] }) + const attributedContent = yelement.getContentDeep(attributionManager) + console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.assert(attributedContent.nodeName === 'UNDEFINED') + }) }) } - From 527e382f8ac966e8b09cd02ca516e81d0ffae8e7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 29 Apr 2025 22:42:56 +0200 Subject: [PATCH 292/362] implement createAttributionsManagerFromDiff that automatically handles gc --- src/index.js | 5 +- src/types/AbstractType.js | 6 +-- src/types/YText.js | 4 +- src/types/YXmlElement.js | 2 +- src/utils/AttributionManager.js | 81 +++++++++++++++++++++++++++++++-- src/utils/IdMap.js | 2 +- src/utils/IdSet.js | 4 +- tests/IdMap.tests.js | 4 +- tests/testHelper.js | 2 +- tests/y-xml.tests.js | 51 +++++++++++++++++++-- 10 files changed, 140 insertions(+), 21 deletions(-) diff --git a/src/index.js b/src/index.js index b7df91036..4dfa0b3c2 100644 --- a/src/index.js +++ b/src/index.js @@ -104,7 +104,7 @@ export { createDeleteSetFromStructStore, IdMap, createIdMap, - createAttribution, + createAttributionItem, createInsertionSetFromStructStore, diffIdMap, diffIdSet, @@ -112,7 +112,8 @@ export { encodeIdMap, createIdMapFromIdSet, TwosetAttributionManager, - noAttributionsManager + noAttributionsManager, + createAttributionManagerFromDiff } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 2f1b6f7e5..7f4161d71 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -10,7 +10,7 @@ import { ContentAny, ContentBinary, getItemCleanStart, - ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttrs, AbstractAttributionManager, // eslint-disable-line + ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, // eslint-disable-line } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -518,7 +518,7 @@ export const typeListGetContent = (type, am) => { } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] - const attribution = createAttributionFromAttrs(attrs, deleted) + const attribution = createAttributionFromAttributionItems(attrs, deleted) d.insert(content.getContent(), null, attribution) } } @@ -1006,7 +1006,7 @@ export const typeMapGetContent = (parent, am) => { am.readContent(cs, item) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) - const attribution = createAttributionFromAttrs(attrs, deleted) + const attribution = createAttributionFromAttributionItems(attrs, deleted) if (deleted) { mapcontent[key] = { prevValue: c, value: undefined, attribution } } else { diff --git a/src/types/YText.js b/src/types/YText.js index fa32cc801..6e08563f9 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -27,7 +27,7 @@ import { ContentType, warnPrematureAccess, noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, // eslint-disable-line - createAttributionFromAttrs + createAttributionFromAttributionItems } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -1038,7 +1038,7 @@ export class YText extends AbstractType { } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] - const attribution = createAttributionFromAttrs(attrs, deleted) + const attribution = createAttributionFromAttributionItems(attrs, deleted) switch (content.constructor) { case ContentString: { d.insert(/** @type {ContentString} */ (content).str, null, attribution) diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index aca5fbb05..25e03aa65 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -254,7 +254,7 @@ export class YXmlElement extends YXmlFragment { getContent (am = noAttributionsManager) { const attributes = typeMapGetContent(this, am) const { children } = super.getContent(am) - return { children, attributes } + return { nodeName: this.nodeName, children, attributes } } /** diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 7ae17eb5f..3373c91c4 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -1,5 +1,12 @@ import { - Item, AbstractContent, IdMap // eslint-disable-line + getItem, + diffIdSet, + createInsertionSetFromStructStore, + createDeleteSetFromStructStore, + createIdMapFromIdSet, + ContentDeleted, + Doc, Item, AbstractContent, IdMap, // eslint-disable-line + findIndexCleanStart } from '../internals.js' import * as error from 'lib0/error' @@ -21,7 +28,7 @@ import * as error from 'lib0/error' * @param {boolean} deleted - whether the attributed item is deleted * @return {Attribution?} */ -export const createAttributionFromAttrs = (attrs, deleted) => { +export const createAttributionFromAttributionItems = (attrs, deleted) => { /** * @type {Attribution?} */ @@ -84,8 +91,6 @@ export class AbstractAttributionManager { } /** - * Abstract class for associating Attributions to content / changes - * * @implements AbstractAttributionManager */ export class TwosetAttributionManager { @@ -136,3 +141,71 @@ export class NoAttributionsManager { } export const noAttributionsManager = new NoAttributionsManager() + +/** + * @implements AbstractAttributionManager + */ +export class DiffAttributionManager { + /** + * @param {IdMap} inserts + * @param {IdMap} deletes + * @param {Doc} prevDoc + * @param {Doc} nextDoc + */ + constructor (inserts, deletes, prevDoc, nextDoc) { + this.inserts = inserts + this.deletes = deletes + this._prevDocStore = prevDoc.store + this._nextDoc = nextDoc + } + + /** + * @param {Array>} contents + * @param {Item} item + */ + readContent (contents, item) { + const deleted = item.deleted || /** @type {any} */ (item.parent).doc !== this._nextDoc + const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) + let content = slice.length === 1 ? item.content : item.content.copy() + if (content instanceof ContentDeleted && slice[0].attrs != null) { + // Retrieved item is never more fragmented than the newer item. + const prevItem = getItem(this._prevDocStore, item.id) + content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content + // trim itemContent to the correct size. + const diffStart = prevItem.id.clock - item.id.clock + const diffEnd = prevItem.id.clock + prevItem.length - item.id.clock - item.length + if (diffStart > 0) { + content = content.splice(diffStart) + } + if (diffEnd > 0) { + content.splice(content.getLength() - diffEnd) + } + } + slice.forEach(s => { + const c = content + if (s.len < c.getLength()) { + content = c.splice(s.len) + } + if (!deleted || s.attrs != null) { + contents.push(new AttributedContent(c, deleted, s.attrs)) + } + }) + } +} + + + +/** + * Attribute changes from ydoc1 to ydoc2. + * + * @param {Doc} prevDoc + * @param {Doc} nextDoc + */ +export const createAttributionManagerFromDiff = (prevDoc, nextDoc) => { + const inserts = diffIdSet(createInsertionSetFromStructStore(nextDoc.store), createInsertionSetFromStructStore(prevDoc.store)) + const deletes = diffIdSet(createDeleteSetFromStructStore(nextDoc.store), createDeleteSetFromStructStore(prevDoc.store)) + const insertMap = createIdMapFromIdSet(inserts, []) + const deleteMap = createIdMapFromIdSet(deletes, []) + // @todo, get deletes from the older doc + return new DiffAttributionManager(insertMap, deleteMap, prevDoc, nextDoc) +} diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 335657c07..7ec75aec2 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -49,7 +49,7 @@ const _hashAttribution = attr => { * @param {V} val * @return {AttributionItem} */ -export const createAttribution = (name, val) => new AttributionItem(name, val) +export const createAttributionItem = (name, val) => new AttributionItem(name, val) /** * @template T diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 82fda2999..f225e750e 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -312,11 +312,11 @@ export const _diffSet = (set, exclude) => { } /** - * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not + * Remove all ranges from `exclude` from `idSet`. The result is a fresh IdSet containing all ranges from `idSet` that are not * in `exclude`. * * @template {IdSet} Set - * @param {Set} set + * @param {Set} idSet * @param {IdSet | IdMap} exclude * @return {Set} */ diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index cf7ddae7a..82cbcad1a 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,6 +1,6 @@ import * as t from 'lib0/testing' import * as idmap from '../src/utils/IdMap.js' -import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js' +import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttributionItem } from './testHelper.js' import * as YY from '../src/internals.js' /** @@ -10,7 +10,7 @@ import * as YY from '../src/internals.js' const simpleConstructAttrs = ops => { const attrs = createIdMap() ops.forEach(op => { - attrs.add(op[0], op[1], op[2], op[3].map(v => createAttribution('', v))) + attrs.add(op[0], op[1], op[2], op[3].map(v => createAttributionItem('', v))) }) return attrs } diff --git a/tests/testHelper.js b/tests/testHelper.js index da0eef70e..14b6b949e 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -436,7 +436,7 @@ export const createRandomIdMap = (gen, clients, clockRange, attrChoices) => { attrs.push(a) } } - idMap.add(client, clockStart, len, attrs.map(v => Y.createAttribution('', v))) + idMap.add(client, clockStart, len, attrs.map(v => Y.createAttributionItem('', v))) } t.info(`Created IdMap with ${numOfOps} ranges and ${attrChoices.length} different attributes. Encoded size: ${encodeIdMap(idMap).byteLength}`) return idMap diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 72dd9dc4a..c3d0fd335 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -284,9 +284,54 @@ export const testElementAttributedContent = _tc => { null, { delete: [] } ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) - .insert([ - delta.createTextDelta().insert('world', null, { insert: [] }) - ], null, { insert: [] }) + .insert([ + delta.createTextDelta().insert('world', null, { insert: [] }) + ], null, { insert: [] }) + const attributedContent = yelement.getContentDeep(attributionManager) + console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.assert(attributedContent.nodeName === 'UNDEFINED') + }) + }) +} + +/** + * @param {t.TestCase} _tc + */ +export const testElementAttributedContentViaDiffer = _tc => { + const ydocV1 = new Y.Doc() + ydocV1.getXmlElement('p').insert(0, [new Y.XmlText('hello'), new Y.XmlElement('span')]) + const ydoc = new Y.Doc() + Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocV1)) + const yelement = ydoc.getXmlElement('p') + const elem1 = yelement.get(0) // new Y.XmlText('hello') + const elem2 = yelement.get(1) // new Y.XmlElement('span') + const elem3 = new Y.XmlText('world') + t.group('insert / delete', () => { + ydoc.transact(() => { + yelement.delete(0, 1) + yelement.insert(1, [elem3]) + yelement.setAttribute('key', '42') + }) + const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) + const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello', null, { delete: [] })], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) + const attributedContent = yelement.getContentDeep(attributionManager) + console.log('children', attributedContent.children.toJSON().ops) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.group('test getContentDeep', () => { + const expectedContent = delta.createArrayDelta().insert( + [delta.createTextDelta().insert('hello', null, { delete: [] })], + null, + { delete: [] } + ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + .insert([ + delta.createTextDelta().insert('world', null, { insert: [] }) + ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) From af2ccc741fbb9d1a2cfa86aec3cd9002135be1af Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 29 Apr 2025 23:29:02 +0200 Subject: [PATCH 293/362] add an simple attributions example --- src/types/YXmlFragment.js | 9 +++-- tests/y-xml.tests.js | 71 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index c60f11ce3..3d33c9da5 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -23,6 +23,7 @@ import { typeListGetContent } from '../internals.js' +import * as delta from '../utils/Delta.js' import * as error from 'lib0/error' import * as array from 'lib0/array' @@ -397,11 +398,15 @@ export class YXmlFragment extends AbstractType { * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} */ getContentDeep (am) { - const { children: origChildren } = this.getContent() + const { children: origChildren } = this.getContent(am) /** * @type {import('../utils/Delta.js').ArrayDelta>} */ - const children = origChildren.map(d => /** @type {any} */ (d instanceof AbstractType ? d.getContentDeep(am) : d)) + const children = origChildren.map(d => /** @type {any} */ ( + d instanceof delta.InsertOp && d.insert instanceof Array + ? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) + : d + )) return { children } } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index c3d0fd335..08c2326e1 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -342,3 +342,74 @@ export const testElementAttributedContentViaDiffer = _tc => { }) }) } + +/** + * @param {t.TestCase} _tc + */ +export const testAttributionManagerSimpleExample = _tc => { +const ydoc = new Y.Doc() +// create some initial content +ydoc.getXmlFragment().insert(0, [new Y.XmlText('hello world')]) +const ydocFork = new Y.Doc() +Y.applyUpdate(ydocFork, Y.encodeStateAsUpdate(ydoc)) +// modify the fork +// append a span element +ydocFork.getXmlFragment().insert(1, [new Y.XmlElement('span')]) +const ytext = /** @type {Y.XmlText} */ (ydocFork.getXmlFragment().get(0)) +// make "hello" italic +ytext.format(0, 5, { italic: true }) +ytext.insert(11, '!') +// highlight the changes +console.log(JSON.stringify(ydocFork.getXmlFragment().getContentDeep(Y.createAttributionManagerFromDiff(ydoc, ydocFork)), null, 2)) +/* => +{ + "children": { + "ops": [ + { + "insert": [ + { + "ops": [ + { + "insert": "hello", + "attributes": { + "italic": true + }, + "attribution": { + "attributes": { + "italic": [] -- the attribute "italic" was changed + } + } + }, + { + "insert": " world" -- "world" remains unchanged + }, + { + "insert": "!", + "attribution": { + "insert": [] -- "!" was inserted + } + } + ] + } + ] + }, + { + "insert": [ + { + "nodeName": "span", + "children": { + "ops": [] + }, + "attributes": {} + } + ], + "attribution": { + "insert": [] -- A tag was inserted + } + } + ] + } +} +*/ +} + From df4a42479307960a6257d799994301a54fba0a99 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Apr 2025 18:13:54 +0200 Subject: [PATCH 294/362] lint --- src/utils/AttributionManager.js | 5 +---- tests/y-xml.tests.js | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 3373c91c4..4b53bb167 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -5,8 +5,7 @@ import { createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, - Doc, Item, AbstractContent, IdMap, // eslint-disable-line - findIndexCleanStart + Doc, Item, AbstractContent, IdMap // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -193,8 +192,6 @@ export class DiffAttributionManager { } } - - /** * Attribute changes from ydoc1 to ydoc2. * diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 08c2326e1..80bb2493a 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -307,7 +307,6 @@ export const testElementAttributedContentViaDiffer = _tc => { const ydoc = new Y.Doc() Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocV1)) const yelement = ydoc.getXmlElement('p') - const elem1 = yelement.get(0) // new Y.XmlText('hello') const elem2 = yelement.get(1) // new Y.XmlElement('span') const elem3 = new Y.XmlText('world') t.group('insert / delete', () => { @@ -347,20 +346,20 @@ export const testElementAttributedContentViaDiffer = _tc => { * @param {t.TestCase} _tc */ export const testAttributionManagerSimpleExample = _tc => { -const ydoc = new Y.Doc() -// create some initial content -ydoc.getXmlFragment().insert(0, [new Y.XmlText('hello world')]) -const ydocFork = new Y.Doc() -Y.applyUpdate(ydocFork, Y.encodeStateAsUpdate(ydoc)) -// modify the fork -// append a span element -ydocFork.getXmlFragment().insert(1, [new Y.XmlElement('span')]) -const ytext = /** @type {Y.XmlText} */ (ydocFork.getXmlFragment().get(0)) -// make "hello" italic -ytext.format(0, 5, { italic: true }) -ytext.insert(11, '!') -// highlight the changes -console.log(JSON.stringify(ydocFork.getXmlFragment().getContentDeep(Y.createAttributionManagerFromDiff(ydoc, ydocFork)), null, 2)) + const ydoc = new Y.Doc() + // create some initial content + ydoc.getXmlFragment().insert(0, [new Y.XmlText('hello world')]) + const ydocFork = new Y.Doc() + Y.applyUpdate(ydocFork, Y.encodeStateAsUpdate(ydoc)) + // modify the fork + // append a span element + ydocFork.getXmlFragment().insert(1, [new Y.XmlElement('span')]) + const ytext = /** @type {Y.XmlText} */ (ydocFork.getXmlFragment().get(0)) + // make "hello" italic + ytext.format(0, 5, { italic: true }) + ytext.insert(11, '!') + // highlight the changes + console.log(JSON.stringify(ydocFork.getXmlFragment().getContentDeep(Y.createAttributionManagerFromDiff(ydoc, ydocFork)), null, 2)) /* => { "children": { @@ -412,4 +411,3 @@ console.log(JSON.stringify(ydocFork.getXmlFragment().getContentDeep(Y.createAttr } */ } - From 574892be8c9cd386532b14683509326e16168ed0 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Apr 2025 18:15:35 +0200 Subject: [PATCH 295/362] 14.0.0-2 --- package-lock.json | 4860 --------------------------------------------- package.json | 2 +- 2 files changed, 1 insertion(+), 4861 deletions(-) delete mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 498cb9330..000000000 --- a/package-lock.json +++ /dev/null @@ -1,4860 +0,0 @@ -{ - "name": "yjs", - "version": "13.6.27", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "yjs", - "version": "13.6.27", - "license": "MIT", - "dependencies": { - "lib0": "^0.2.105", - "y-protocols": "^1.0.5" - }, - "devDependencies": { - "@types/node": "^22.14.1", - "concurrently": "^3.6.1", - "jsdoc": "^3.6.7", - "markdownlint-cli": "^0.41.0", - "rollup": "^4.37.0", - "standard": "^16.0.4", - "tui-jsdoc-template": "^1.2.2", - "typescript": "^5.8.3", - "yjs": "." - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", - "integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz", - "integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz", - "integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz", - "integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz", - "integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz", - "integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz", - "integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz", - "integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz", - "integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz", - "integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz", - "integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz", - "integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz", - "integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz", - "integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz", - "integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz", - "integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz", - "integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz", - "integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz", - "integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz", - "integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/linkify-it": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.5.tgz", - "integrity": "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.5.tgz", - "integrity": "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz", - "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz", - "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "set-function-length": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", - "dev": true, - "dependencies": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cheerio/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/commander": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", - "dev": true, - "engines": { - "node": ">= 0.6.x" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concurrently": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.1.tgz", - "integrity": "sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q==", - "dev": true, - "dependencies": { - "chalk": "^2.4.1", - "commander": "2.6.0", - "date-fns": "^1.23.0", - "lodash": "^4.5.1", - "read-pkg": "^3.0.0", - "rx": "2.3.24", - "spawn-command": "^0.0.2-1", - "supports-color": "^3.2.3", - "tree-kill": "^1.1.0" - }, - "bin": { - "concurrent": "src/main.js", - "concurrently": "src/main.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", - "dev": true, - "dependencies": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "node_modules/css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true - }, - "node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-data-property": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz", - "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "dev": true, - "dependencies": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true - }, - "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1 || ^5.0.0" - } - }, - "node_modules/eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-react": "^7.21.5" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "dev": true, - "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.6.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.11.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", - "dev": true, - "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" - } - }, - "node_modules/eslint-plugin-node/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-node/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-promise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", - "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", - "dev": true, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", - "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", - "doctrine": "^2.1.0", - "estraverse": "^5.2.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.hasown": "^1.0.0", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7" - } - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isomorphic.js": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", - "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/js-yaml/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lib0": { - "version": "0.2.105", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.105.tgz", - "integrity": "sha512-5vtbuBi2P43ZYOfVMV+TZYkWEa0J9kijXirzEgrPA+nJDQCtMx805/rqW4G1nXbM9IRIhwW+OyNNgcQdbhKfSw==", - "license": "MIT", - "dependencies": { - "isomorphic.js": "^0.2.4" - }, - "bin": { - "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", - "0gentesthtml": "bin/gentesthtml.js", - "0serve": "bin/0serve.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - } - }, - "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dev": true, - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", - "dev": true - }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==", - "dev": true - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "dev": true - }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==", - "dev": true - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", - "dev": true - }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", - "dev": true - }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "dev": true - }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", - "dev": true - }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==", - "dev": true - }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", - "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", - "dev": true, - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" - } - }, - "node_modules/markdownlint": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz", - "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==", - "dev": true, - "dependencies": { - "markdown-it": "14.1.0", - "markdownlint-micromark": "0.1.9" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, - "node_modules/markdownlint-cli": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz", - "integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==", - "dev": true, - "dependencies": { - "commander": "~12.1.0", - "get-stdin": "~9.0.0", - "glob": "~10.4.1", - "ignore": "~5.3.1", - "js-yaml": "^4.1.0", - "jsonc-parser": "~3.2.1", - "jsonpointer": "5.0.1", - "markdownlint": "~0.34.0", - "minimatch": "~9.0.4", - "run-con": "~1.3.2", - "smol-toml": "~1.2.0" - }, - "bin": { - "markdownlint": "markdownlint.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/markdownlint-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/markdownlint-cli/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/markdownlint-micromark": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz", - "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, - "node_modules/markdownlint/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/markdownlint/node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "dev": true, - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/markdownlint/node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/markdownlint/node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true - }, - "node_modules/markdownlint/node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "dependencies": { - "boolbase": "~1.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true - }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", - "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", - "dev": true, - "dependencies": { - "find-up": "^3.0.0", - "load-json-file": "^5.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", - "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", - "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/rollup": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz", - "integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.37.0", - "@rollup/rollup-android-arm64": "4.37.0", - "@rollup/rollup-darwin-arm64": "4.37.0", - "@rollup/rollup-darwin-x64": "4.37.0", - "@rollup/rollup-freebsd-arm64": "4.37.0", - "@rollup/rollup-freebsd-x64": "4.37.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", - "@rollup/rollup-linux-arm-musleabihf": "4.37.0", - "@rollup/rollup-linux-arm64-gnu": "4.37.0", - "@rollup/rollup-linux-arm64-musl": "4.37.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", - "@rollup/rollup-linux-riscv64-gnu": "4.37.0", - "@rollup/rollup-linux-riscv64-musl": "4.37.0", - "@rollup/rollup-linux-s390x-gnu": "4.37.0", - "@rollup/rollup-linux-x64-gnu": "4.37.0", - "@rollup/rollup-linux-x64-musl": "4.37.0", - "@rollup/rollup-win32-arm64-msvc": "4.37.0", - "@rollup/rollup-win32-ia32-msvc": "4.37.0", - "@rollup/rollup-win32-x64-msvc": "4.37.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-con": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", - "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", - "dev": true, - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~4.1.0", - "minimist": "^1.2.8", - "strip-json-comments": "~3.1.1" - }, - "bin": { - "run-con": "cli.js" - } - }, - "node_modules/rx": { - "version": "2.3.24", - "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", - "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", - "dev": true - }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/set-function-length": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", - "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.2", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", - "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/smol-toml": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.2.tgz", - "integrity": "sha512-fVEjX2ybKdJKzFL46VshQbj9PuA4IUKivalgp48/3zwS9vXzyykzQ6AX92UxHSvWJagziMRLeHMgEzoGO7A8hQ==", - "dev": true, - "engines": { - "node": ">= 18" - } - }, - "node_modules/spawn-command": { - "version": "0.0.2-1", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", - "integrity": "sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz", - "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", - "dev": true - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/standard": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", - "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "eslint": "~7.18.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "~2.24.2", - "eslint-plugin-node": "~11.1.0", - "eslint-plugin-promise": "~5.1.0", - "eslint-plugin-react": "~7.25.1", - "standard-engine": "^14.0.1" - }, - "bin": { - "standard": "bin/cmd.js" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "get-stdin": "^8.0.0", - "minimist": "^1.2.5", - "pkg-conf": "^3.1.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8.10" - } - }, - "node_modules/standard-engine/node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", - "dev": true, - "dependencies": { - "has-flag": "^1.0.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", - "dev": true - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tui-jsdoc-template": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tui-jsdoc-template/-/tui-jsdoc-template-1.2.2.tgz", - "integrity": "sha512-oqw0IYaot86VJ2owKBozJnilgta0Z55x8r9PeHj7vb+jDoSvJGRUQUcgs56SZh9HE20fx54Pe75p84X85/ygLA==", - "dev": true, - "dependencies": { - "cheerio": "^0.22.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", - "integrity": "sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", - "dev": true - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", - "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.6", - "call-bind": "^1.0.5", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, - "node_modules/y-protocols": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", - "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", - "dependencies": { - "lib0": "^0.2.85" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=8.0.0" - }, - "funding": { - "type": "GitHub Sponsors ❤", - "url": "https://github.com/sponsors/dmonad" - }, - "peerDependencies": { - "yjs": "^13.0.0" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yjs": { - "resolved": "", - "link": true - } - } -} diff --git a/package.json b/package.json index da7b1ab19..2b26567b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.6.27", + "version": "14.0.0-2", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2daad96c12aa637a2c84fdee327f72a240b390ac Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Apr 2025 22:12:09 +0200 Subject: [PATCH 296/362] implement idset.delete & idmap.delete --- src/structs/ContentType.js | 4 +-- src/structs/Item.js | 4 +-- src/utils/IdMap.js | 25 +++++++++++-- src/utils/IdSet.js | 64 ++++++++++++++++++++++++++++++++-- src/utils/PermanentUserData.js | 2 +- src/utils/Snapshot.js | 2 +- src/utils/Transaction.js | 2 +- src/utils/UndoManager.js | 2 +- src/utils/YEvent.js | 4 +-- tests/IdMap.tests.js | 56 ++++++++++++++++++++--------- tests/IdSet.tests.js | 26 ++++++++++++-- 11 files changed, 156 insertions(+), 35 deletions(-) diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index 5f1c71f92..597c87190 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -107,7 +107,7 @@ export class ContentType { while (item !== null) { if (!item.deleted) { item.delete(transaction) - } else if (!transaction.insertSet.has(item.id)) { + } else if (!transaction.insertSet.hasId(item.id)) { // This will be gc'd later and we want to merge it if possible // We try to merge all deleted items after each transaction, // but we have no knowledge about that this needs to be merged @@ -119,7 +119,7 @@ export class ContentType { this.type._map.forEach(item => { if (!item.deleted) { item.delete(transaction) - } else if (!transaction.insertSet.has(item.id)) { + } else if (!transaction.insertSet.hasId(item.id)) { // same as above transaction._mergeStructs.push(item) } diff --git a/src/structs/Item.js b/src/structs/Item.js index 8ca713c3d..838d6b118 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -124,7 +124,7 @@ export const splitItem = (transaction, leftItem, diff) => { * @param {Array} stack * @param {ID} id */ -const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => s.deletions.has(id)) +const isDeletedByUndoStack = (stack, id) => array.some(stack, /** @param {StackItem} s */ s => s.deletions.hasId(id)) /** * Redoes the effect of this operation. @@ -210,7 +210,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo left = item // Iterate right while right is in itemsToDelete // If it is intended to delete right while item is redone, we can expect that item should replace right. - while (left !== null && left.right !== null && (left.right.redone || itemsToDelete.has(left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) { + while (left !== null && left.right !== null && (left.right.redone || itemsToDelete.hasId(left.right.id) || isDeletedByUndoStack(um.undoStack, left.right.id) || isDeletedByUndoStack(um.redoStack, left.right.id))) { left = left.right // follow redone while (left.redone) left = getItemCleanStart(transaction, left.redone) diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 7ec75aec2..5f6741828 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -2,6 +2,7 @@ import { _diffSet, findIndexInIdRanges, findRangeStartInIdRanges, + _deleteRangeFromIdSet, DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID // eslint-disable-line } from '../internals.js' @@ -319,10 +320,19 @@ export class IdMap { * @param {ID} id * @return {boolean} */ - has (id) { - const dr = this.clients.get(id.client) + hasId (id) { + return this.has(id.client, id.clock) + } + + /** + * @param {number} client + * @param {number} clock + * @return {boolean} + */ + has (client, clock) { + const dr = this.clients.get(client) if (dr) { - return findIndexInIdRanges(dr.getIds(), id.clock) !== null + return findIndexInIdRanges(dr.getIds(), clock) !== null } return false } @@ -395,6 +405,15 @@ export class IdMap { ranges.add(clock, len, attrs) } } + + /** + * @param {number} client + * @param {number} clock + * @param {number} len + */ + delete (client, clock, len) { + _deleteRangeFromIdSet(this, client, clock, len) + } } /** diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index f225e750e..3d1551632 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -113,13 +113,71 @@ export class IdSet { * @param {ID} id * @return {boolean} */ - has (id) { - const dr = this.clients.get(id.client) + hasId (id) { + return this.has(id.client, id.clock) + } + + /** + * @param {number} client + * @param {number} clock + */ + has (client, clock) { + const dr = this.clients.get(client) if (dr) { - return findIndexInIdRanges(dr.getIds(), id.clock) !== null + return findIndexInIdRanges(dr.getIds(), clock) !== null } return false } + + /** + * @param {number} client + * @param {number} clock + * @param {number} len + */ + add (client, clock, len) { + addToIdSet(this, client, clock, len) + } + + /** + * @param {number} client + * @param {number} clock + * @param {number} len + */ + delete (client, clock, len) { + _deleteRangeFromIdSet(this, client, clock, len) + } +} + +/** + * @param {IdSet | IdMap} set + * @param {number} client + * @param {number} clock + * @param {number} len + */ +export const _deleteRangeFromIdSet = (set, client, clock, len) => { + const dr = set.clients.get(client) + if (dr && len > 0) { + const ids = dr.getIds() + let index = findRangeStartInIdRanges(ids, clock) + if (index != null) { + for (let r = ids[index]; index < ids.length && r.clock < clock + len; r = ids[++index]) { + if (r.clock < clock) { + ids[index] = r.copyWith(r.clock, clock-r.clock) + if (clock + len < r.clock + r.len) { + ids.splice(index + 1, 0, r.copyWith(clock + len, r.clock + r.len - clock - len)) + } + } else if (clock + len < r.clock + r.len) { + // need to retain end + ids[index] = r.copyWith(clock + len , r.clock + r.len - clock - len) + } else if (ids.length === 1) { + set.clients.delete(client) + return + } else { + ids.splice(index--, 1) + } + } + } + } } /** diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js index fca1b663a..d4067fde1 100644 --- a/src/utils/PermanentUserData.js +++ b/src/utils/PermanentUserData.js @@ -131,7 +131,7 @@ export class PermanentUserData { */ getUserByDeletedId (id) { for (const [userDescription, ds] of this.dss.entries()) { - if (ds.has(id)) { + if (ds.hasId(id)) { return userDescription } } diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index fd21a37b4..fcf9ea0a2 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -117,7 +117,7 @@ export const snapshot = doc => createSnapshot(createDeleteSetFromStructStore(doc */ export const isVisible = (item, snapshot) => snapshot === undefined ? !item.deleted - : snapshot.sv.has(item.id.client) && (snapshot.sv.get(item.id.client) || 0) > item.id.clock && !snapshot.ds.has(item.id) + : snapshot.sv.has(item.id.client) && (snapshot.sv.get(item.id.client) || 0) > item.id.clock && !snapshot.ds.hasId(item.id) /** * @param {Transaction} transaction diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index c90f3b768..5f9354375 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -199,7 +199,7 @@ export const nextID = transaction => { */ export const addChangedTypeToTransaction = (transaction, type, parentSub) => { const item = type._item - if (item === null || (!item.deleted && !transaction.insertSet.has(item.id))) { + if (item === null || (!item.deleted && !transaction.insertSet.hasId(item.id))) { map.setIfUndefined(transaction.changed, type, set.create).add(parentSub) } } diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 6fef6aa86..09cdc4b74 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -89,7 +89,7 @@ const popStackItem = (undoManager, stack, eventType) => { struct instanceof Item && scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType} */ (type), struct)) && // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval. - !stackItem.insertions.has(struct.id) + !stackItem.insertions.hasId(struct.id) ) { itemsToRedo.add(struct) } diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index deffd7ea6..c8f6e3605 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -77,7 +77,7 @@ export class YEvent { * @return {boolean} */ deletes (struct) { - return this.transaction.deleteSet.has(struct.id) + return this.transaction.deleteSet.hasId(struct.id) } /** @@ -157,7 +157,7 @@ export class YEvent { * @return {boolean} */ adds (struct) { - return this.transaction.insertSet.has(struct.id) + return this.transaction.insertSet.hasId(struct.id) } /** diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 82cbcad1a..54d2cbcb2 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -1,6 +1,8 @@ import * as t from 'lib0/testing' import * as idmap from '../src/utils/IdMap.js' -import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttributionItem } from './testHelper.js' +import * as prng from 'lib0/prng' +import * as math from 'lib0/math' +import { compareIdmaps as compareIdMaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttributionItem } from './testHelper.js' import * as YY from '../src/internals.js' /** @@ -21,49 +23,49 @@ const simpleConstructAttrs = ops => { export const testAmMerge = _tc => { const attrs = [42] t.group('filter out empty items (1))', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 0, attrs]]), simpleConstructAttrs([]) ) }) t.group('filter out empty items (2))', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 0, attrs], [0, 2, 0, attrs]]), simpleConstructAttrs([]) ) }) t.group('filter out empty items (3 - end))', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 1, attrs], [0, 2, 0, attrs]]), simpleConstructAttrs([[0, 1, 1, attrs]]) ) }) t.group('filter out empty items (4 - middle))', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 1, attrs], [0, 2, 0, attrs], [0, 3, 1, attrs]]), simpleConstructAttrs([[0, 1, 1, attrs], [0, 3, 1, attrs]]) ) }) t.group('filter out empty items (5 - beginning))', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 0, attrs], [0, 2, 1, attrs], [0, 3, 1, attrs]]), simpleConstructAttrs([[0, 2, 1, attrs], [0, 3, 1, attrs]]) ) }) t.group('merge of overlapping id ranges', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 2, attrs], [0, 0, 2, attrs]]), simpleConstructAttrs([[0, 0, 3, attrs]]) ) }) t.group('construct without hole', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 2, attrs], [0, 3, 1, attrs]]), simpleConstructAttrs([[0, 1, 3, attrs]]) ) }) t.group('no merge of overlapping id ranges with different attributes', () => { - compareIdmaps( + compareIdMaps( simpleConstructAttrs([[0, 1, 2, [1]], [0, 0, 2, [2]]]), simpleConstructAttrs([[0, 0, 1, [2]], [0, 1, 1, [1, 2]], [0, 2, 1, [1]]]) ) @@ -85,12 +87,12 @@ export const testRepeatMergingMultipleIdMaps = tc => { } const merged = idmap.mergeIdMaps(sets) const mergedReverse = idmap.mergeIdMaps(sets.reverse()) - compareIdmaps(merged, mergedReverse) + compareIdMaps(merged, mergedReverse) const composed = idmap.createIdMap() for (let iclient = 0; iclient < clients; iclient++) { for (let iclock = 0; iclock < clockRange + 42; iclock++) { - const mergedHas = merged.has(new ID(iclient, iclock)) - const oneHas = sets.some(ids => ids.has(new ID(iclient, iclock))) + const mergedHas = merged.hasId(new ID(iclient, iclock)) + const oneHas = sets.some(ids => ids.hasId(new ID(iclient, iclock))) t.assert(mergedHas === oneHas) const mergedAttrs = merged.slice(new ID(iclient, iclock), 1) mergedAttrs.forEach(a => { @@ -100,7 +102,7 @@ export const testRepeatMergingMultipleIdMaps = tc => { }) } } - compareIdmaps(merged, composed) + compareIdMaps(merged, composed) } /** @@ -115,9 +117,9 @@ export const testRepeatRandomDiffing = tc => { const merged = idmap.mergeIdMaps([idset1, idset2]) const e1 = idmap.diffIdMap(idset1, idset2) const e2 = idmap.diffIdMap(merged, idset2) - compareIdmaps(e1, e2) + compareIdMaps(e1, e2) const copy = YY.decodeIdMap(YY.encodeIdMap(e1)) - compareIdmaps(e1, copy) + compareIdMaps(e1, copy) } /** @@ -135,7 +137,27 @@ export const testRepeatRandomDiffing2 = tc => { const e1 = idmap.diffIdMap(idmap1, idsExclude) const e2 = idmap.diffIdMap(idmap2, idsExclude) const excludedMerged = idmap.mergeIdMaps([e1, e2]) - compareIdmaps(mergedExcluded, excludedMerged) + compareIdMaps(mergedExcluded, excludedMerged) const copy = YY.decodeIdMap(YY.encodeIdMap(mergedExcluded)) - compareIdmaps(mergedExcluded, copy) + compareIdMaps(mergedExcluded, copy) +} + +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDeletes = tc => { + const clients = 1 + const clockRange = 100 + const idset = createRandomIdMap(tc.prng, clients, clockRange, []) + const client = Array.from(idset.clients.keys())[0] + const clock = prng.int31(tc.prng, 0, clockRange) + const len = prng.int31(tc.prng, 0, math.round((clockRange - clock) * 1.2)) // allow exceeding range to cover more edge cases + const idsetOfDeletes = idmap.createIdMap() + idsetOfDeletes.add(client, clock, len, []) + const diffed = idmap.diffIdMap(idset, idsetOfDeletes) + idset.delete(client, clock, len) + for (let i = 0; i < len; i++) { + t.assert(!idset.has(client, clock + i)) + } + compareIdMaps(idset, diffed) } diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index 8078afefa..d55ba352a 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -1,5 +1,7 @@ import * as t from 'lib0/testing' import * as d from '../src/utils/IdSet.js' +import * as math from 'lib0/math' +import * as prng from 'lib0/prng' import { compareIdSets, createRandomIdSet, ID } from './testHelper.js' /** @@ -153,6 +155,26 @@ export const testRepeatRandomDiffing = tc => { compareIdSets(e1, e2) } +/** + * @param {t.TestCase} tc + */ +export const testRepeatRandomDeletes = tc => { + const clients = 1 + const clockRange = 100 + const idset = createRandomIdSet(tc.prng, clients, clockRange) + const client = Array.from(idset.clients.keys())[0] + const clock = prng.int31(tc.prng, 0, clockRange) + const len = prng.int31(tc.prng, 0, math.round((clockRange - clock) * 1.2)) // allow exceeding range to cover more edge cases + const idsetOfDeletes = d.createIdSet() + idsetOfDeletes.add(client, clock, len) + const diffed = d.diffIdSet(idset, idsetOfDeletes) + idset.delete(client, clock, len) + for (let i = 0; i < len; i++) { + t.assert(!idset.has(client, clock + i)) + } + compareIdSets(idset, diffed) +} + /** * @param {t.TestCase} tc */ @@ -172,8 +194,8 @@ export const testRepeatMergingMultipleIdsets = tc => { const composed = d.createIdSet() for (let iclient = 0; iclient < clients; iclient++) { for (let iclock = 0; iclock < clockRange + 42; iclock++) { - const mergedHas = merged.has(new ID(iclient, iclock)) - const oneHas = idss.some(ids => ids.has(new ID(iclient, iclock))) + const mergedHas = merged.hasId(new ID(iclient, iclock)) + const oneHas = idss.some(ids => ids.hasId(new ID(iclient, iclock))) t.assert(mergedHas === oneHas) if (oneHas) { d.addToIdSet(composed, iclient, iclock, 1) From a43f1983c5103b3d9ac66b88d2fc16d68f7c2c4b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Apr 2025 23:17:15 +0200 Subject: [PATCH 297/362] [AttributionManager] auto-updates on doc changes and can destroy itself --- src/utils/AttributionManager.js | 54 +++++++++++++++------ src/utils/IdMap.js | 14 +++++- src/utils/IdSet.js | 30 +++++++----- tests/y-xml.tests.js | 85 +++++++++++++++++++++++---------- 4 files changed, 131 insertions(+), 52 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 4b53bb167..e85f7a565 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -5,7 +5,10 @@ import { createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, - Doc, Item, AbstractContent, IdMap // eslint-disable-line + Doc, Item, AbstractContent, IdMap, // eslint-disable-line + insertIntoIdMap, + insertIntoIdSet, + diffIdMap } from '../internals.js' import * as error from 'lib0/error' @@ -146,16 +149,46 @@ export const noAttributionsManager = new NoAttributionsManager() */ export class DiffAttributionManager { /** - * @param {IdMap} inserts - * @param {IdMap} deletes * @param {Doc} prevDoc * @param {Doc} nextDoc */ - constructor (inserts, deletes, prevDoc, nextDoc) { - this.inserts = inserts - this.deletes = deletes + constructor (prevDoc, nextDoc) { + const nextDocInserts = createInsertionSetFromStructStore(nextDoc.store) + const prevDocInserts = createInsertionSetFromStructStore(prevDoc.store) + const nextDocDeletes = createDeleteSetFromStructStore(nextDoc.store) + const prevDocDeletes = createDeleteSetFromStructStore(prevDoc.store) + this.inserts = createIdMapFromIdSet(diffIdSet(nextDocInserts, prevDocInserts), []) + this.deletes = createIdMapFromIdSet(diffIdSet(nextDocDeletes, prevDocDeletes), []) + + this._prevDoc = prevDoc this._prevDocStore = prevDoc.store this._nextDoc = nextDoc + // update before observer calls fired + this._nextBOH = nextDoc.on('beforeObserverCalls', tr => { + // update inserts + insertIntoIdSet(nextDocInserts, tr.insertSet) + const diffInserts = diffIdSet(tr.insertSet, prevDocInserts) + insertIntoIdMap(this.inserts, createIdMapFromIdSet(diffInserts, [])) + // update deletes + insertIntoIdSet(nextDocDeletes, tr.deleteSet) + const diffDeletes = diffIdSet(tr.deleteSet, prevDocDeletes) + insertIntoIdMap(this.deletes, createIdMapFromIdSet(diffDeletes, [])) + // @todo fire update ranges on `diffInserts` and `diffDeletes` + }) + this._prevBOH = prevDoc.on('beforeObserverCalls', tr => { + this.inserts = diffIdMap(this.inserts, tr.insertSet) + this.deletes = diffIdMap(this.deletes, tr.deleteSet) + // @todo fire update ranges on `tr.insertSet` and `tr.deleteSet` + }) + this._destroyHandler = nextDoc.on('destroy', this.destroy.bind(this)) + prevDoc.on('destroy', this._destroyHandler) + } + + destroy () { + this._nextDoc.off('destroy', this._destroyHandler) + this._prevDoc.off('destroy', this._destroyHandler) + this._nextDoc.off('beforeObserverCalls', this._nextBOH) + this._prevDoc.off('beforeObserverCalls', this._prevBOH) } /** @@ -198,11 +231,4 @@ export class DiffAttributionManager { * @param {Doc} prevDoc * @param {Doc} nextDoc */ -export const createAttributionManagerFromDiff = (prevDoc, nextDoc) => { - const inserts = diffIdSet(createInsertionSetFromStructStore(nextDoc.store), createInsertionSetFromStructStore(prevDoc.store)) - const deletes = diffIdSet(createDeleteSetFromStructStore(nextDoc.store), createDeleteSetFromStructStore(prevDoc.store)) - const insertMap = createIdMapFromIdSet(inserts, []) - const deleteMap = createIdMapFromIdSet(deletes, []) - // @todo, get deletes from the older doc - return new DiffAttributionManager(insertMap, deleteMap, prevDoc, nextDoc) -} +export const createAttributionManagerFromDiff = (prevDoc, nextDoc) => new DiffAttributionManager(prevDoc, nextDoc) diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 5f6741828..9b1687174 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -3,7 +3,8 @@ import { findIndexInIdRanges, findRangeStartInIdRanges, _deleteRangeFromIdSet, - DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID // eslint-disable-line + DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID, // eslint-disable-line + _insertIntoIdSet } from '../internals.js' import * as array from 'lib0/array' @@ -139,6 +140,10 @@ export class AttrRanges { this._ids = ids } + copy () { + return new AttrRanges(this._ids.slice()) + } + /** * @param {number} clock * @param {number} length @@ -572,6 +577,13 @@ const _ensureAttrs = (idmap, attrs) => attrs.map(attr => export const createIdMap = () => new IdMap() +/** + * @template T + * @param {IdMap} dest + * @param {IdMap} src + */ +export const insertIntoIdMap = _insertIntoIdSet + /** * Remove all ranges from `exclude` from `ds`. The result is a fresh IdMap containing all ranges from `idSet` that are not * in `exclude`. diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 3d1551632..393c91b2b 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -51,6 +51,10 @@ class IdRanges { this._ids = ids } + copy () { + return new IdRanges(this._ids.slice()) + } + /** * @param {number} clock * @param {number} length @@ -162,13 +166,13 @@ export const _deleteRangeFromIdSet = (set, client, clock, len) => { if (index != null) { for (let r = ids[index]; index < ids.length && r.clock < clock + len; r = ids[++index]) { if (r.clock < clock) { - ids[index] = r.copyWith(r.clock, clock-r.clock) + ids[index] = r.copyWith(r.clock, clock - r.clock) if (clock + len < r.clock + r.len) { ids.splice(index + 1, 0, r.copyWith(clock + len, r.clock + r.len - clock - len)) } } else if (clock + len < r.clock + r.len) { // need to retain end - ids[index] = r.copyWith(clock + len , r.clock + r.len - clock - len) + ids[index] = r.copyWith(clock + len, r.clock + r.len - clock - len) } else if (ids.length === 1) { set.clients.delete(client) return @@ -283,23 +287,30 @@ export const mergeIdSets = idSets => { } /** - * @param {IdSet} dest - * @param {IdSet} src + * @template {IdSet | IdMap} S + * @param {S} dest + * @param {S} src */ -export const insertIntoIdSet = (dest, src) => { +export const _insertIntoIdSet = (dest, src) => { src.clients.forEach((srcRanges, client) => { const targetRanges = dest.clients.get(client) if (targetRanges) { array.appendTo(targetRanges.getIds(), srcRanges.getIds()) targetRanges.sorted = false } else { - const res = new IdRanges(srcRanges.getIds().slice()) + const res = srcRanges.copy() res.sorted = true - dest.clients.set(client, res) + dest.clients.set(client, /** @type {any} */ (res)) } }) } +/** + * @param {IdSet} dest + * @param {IdSet} src + */ +export const insertIntoIdSet = _insertIntoIdSet + /** * Remove all ranges from `exclude` from `ds`. The result is a fresh IdSet containing all ranges from `idSet` that are not * in `exclude`. @@ -373,10 +384,7 @@ export const _diffSet = (set, exclude) => { * Remove all ranges from `exclude` from `idSet`. The result is a fresh IdSet containing all ranges from `idSet` that are not * in `exclude`. * - * @template {IdSet} Set - * @param {Set} idSet - * @param {IdSet | IdMap} exclude - * @return {Set} + * @type {(idSet: IdSet, exclude: IdSet|IdMap) => IdSet} */ export const diffIdSet = _diffSet diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 80bb2493a..121e289be 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -309,36 +309,69 @@ export const testElementAttributedContentViaDiffer = _tc => { const yelement = ydoc.getXmlElement('p') const elem2 = yelement.get(1) // new Y.XmlElement('span') const elem3 = new Y.XmlText('world') - t.group('insert / delete', () => { - ydoc.transact(() => { - yelement.delete(0, 1) - yelement.insert(1, [elem3]) - yelement.setAttribute('key', '42') - }) - const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) - const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello', null, { delete: [] })], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) + ydoc.transact(() => { + yelement.delete(0, 1) + yelement.insert(1, [elem3]) + yelement.setAttribute('key', '42') + }) + const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) + const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello', null, { delete: [] })], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) + const attributedContent = yelement.getContentDeep(attributionManager) + console.log('children', attributedContent.children.toJSON().ops) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.group('test getContentDeep', () => { + const expectedContent = delta.createArrayDelta().insert( + [delta.createTextDelta().insert('hello', null, { delete: [] })], + null, + { delete: [] } + ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + .insert([ + delta.createTextDelta().insert('world', null, { insert: [] }) + ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', attributedContent.children.toJSON().ops) + console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) - t.group('test getContentDeep', () => { - const expectedContent = delta.createArrayDelta().insert( - [delta.createTextDelta().insert('hello', null, { delete: [] })], - null, - { delete: [] } - ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) - .insert([ - delta.createTextDelta().insert('world', null, { insert: [] }) - ], null, { insert: [] }) - const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) - console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) - console.log('attributes', attributedContent.attributes) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) - t.assert(attributedContent.nodeName === 'UNDEFINED') - }) + t.assert(attributedContent.nodeName === 'UNDEFINED') + }) + ydoc.transact(() => { + elem3.insert(0, 'big') + }) + t.group('test getContentDeep after some more updates', () => { + t.info('expecting diffingAttributionManager to auto update itself') + const expectedContent = delta.createArrayDelta().insert( + [delta.createTextDelta().insert('hello', null, { delete: [] })], + null, + { delete: [] } + ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + .insert([ + delta.createTextDelta().insert('bigworld', null, { insert: [] }) + ], null, { insert: [] }) + const attributedContent = yelement.getContentDeep(attributionManager) + console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.assert(attributedContent.nodeName === 'UNDEFINED') + }) + Y.applyUpdate(ydocV1, Y.encodeStateAsUpdate(ydoc)) + t.group('test getContentDeep both docs synced', () => { + t.info('expecting diffingAttributionManager to auto update itself') + const expectedContent = delta.createArrayDelta().insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]).insert([ + delta.createTextDelta().insert('bigworld') + ]) + const attributedContent = yelement.getContentDeep(attributionManager) + console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('attributes', attributedContent.attributes) + t.assert(attributedContent.children.equals(expectedContent)) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: null } }) + t.assert(attributedContent.nodeName === 'UNDEFINED') }) } From d8143efb125de2c45513c958189b2900596a0e4e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 1 May 2025 14:44:24 +0200 Subject: [PATCH 298/362] fix nicks test --- src/utils/AttributionManager.js | 19 +++++++++---------- src/utils/IdSet.js | 8 +++++--- tests/y-text.tests.js | 4 ++-- tests/y-xml.tests.js | 4 ++++ 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index e85f7a565..f363d98ff 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -153,29 +153,28 @@ export class DiffAttributionManager { * @param {Doc} nextDoc */ constructor (prevDoc, nextDoc) { - const nextDocInserts = createInsertionSetFromStructStore(nextDoc.store) - const prevDocInserts = createInsertionSetFromStructStore(prevDoc.store) - const nextDocDeletes = createDeleteSetFromStructStore(nextDoc.store) - const prevDocDeletes = createDeleteSetFromStructStore(prevDoc.store) - this.inserts = createIdMapFromIdSet(diffIdSet(nextDocInserts, prevDocInserts), []) + const _nextDocInserts = createInsertionSetFromStructStore(nextDoc.store, false) // unmaintained + const _prevDocInserts = createInsertionSetFromStructStore(prevDoc.store, false) // unmaintained + const nextDocDeletes = createDeleteSetFromStructStore(nextDoc.store) // maintained + const prevDocDeletes = createDeleteSetFromStructStore(prevDoc.store) // maintained + this.inserts = createIdMapFromIdSet(diffIdSet(_nextDocInserts, _prevDocInserts), []) this.deletes = createIdMapFromIdSet(diffIdSet(nextDocDeletes, prevDocDeletes), []) - this._prevDoc = prevDoc this._prevDocStore = prevDoc.store this._nextDoc = nextDoc // update before observer calls fired this._nextBOH = nextDoc.on('beforeObserverCalls', tr => { // update inserts - insertIntoIdSet(nextDocInserts, tr.insertSet) - const diffInserts = diffIdSet(tr.insertSet, prevDocInserts) + const diffInserts = diffIdSet(tr.insertSet, _prevDocInserts) insertIntoIdMap(this.inserts, createIdMapFromIdSet(diffInserts, [])) // update deletes - insertIntoIdSet(nextDocDeletes, tr.deleteSet) const diffDeletes = diffIdSet(tr.deleteSet, prevDocDeletes) insertIntoIdMap(this.deletes, createIdMapFromIdSet(diffDeletes, [])) // @todo fire update ranges on `diffInserts` and `diffDeletes` }) this._prevBOH = prevDoc.on('beforeObserverCalls', tr => { + insertIntoIdSet(_prevDocInserts, tr.insertSet) + insertIntoIdSet(prevDocDeletes, tr.deleteSet) this.inserts = diffIdMap(this.inserts, tr.insertSet) this.deletes = diffIdMap(this.deletes, tr.deleteSet) // @todo fire update ranges on `tr.insertSet` and `tr.deleteSet` @@ -199,7 +198,7 @@ export class DiffAttributionManager { const deleted = item.deleted || /** @type {any} */ (item.parent).doc !== this._nextDoc const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() - if (content instanceof ContentDeleted && slice[0].attrs != null) { + if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.hasId(item.id)) { // Retrieved item is never more fragmented than the newer item. const prevItem = getItem(this._prevDocStore, item.id) content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 393c91b2b..d9f06d814 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -454,8 +454,9 @@ export const createDeleteSetFromStructStore = ss => { /** * @param {import('../internals.js').StructStore} ss + * @param {boolean} filterDeleted */ -export const createInsertionSetFromStructStore = ss => { +export const createInsertionSetFromStructStore = (ss, filterDeleted) => { const idset = createIdSet() ss.clients.forEach((structs, client) => { /** @@ -464,11 +465,12 @@ export const createInsertionSetFromStructStore = ss => { const iditems = [] for (let i = 0; i < structs.length; i++) { const struct = structs[i] - if (!struct.deleted) { + if (!(filterDeleted && struct.deleted)) { const clock = struct.id.clock let len = struct.length if (i + 1 < structs.length) { - for (let next = structs[i + 1]; i + 1 < structs.length && !next.deleted; next = structs[++i + 1]) { + // eslint-disable-next-line + for (let next = structs[i + 1]; i + 1 < structs.length && !(filterDeleted && next.deleted); next = structs[++i + 1]) { len += next.length } } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 95bb3837d..50c5f6e60 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2343,10 +2343,10 @@ export const testAttributedDiffing = _tc => { const ytext = ydoc.getText() ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) // this represents to all insertions of ydoc - const insertionSet = Y.createInsertionSetFromStructStore(ydoc.store) + const insertionSet = Y.createInsertionSetFromStructStore(ydoc.store, false) const deleteSet = Y.createDeleteSetFromStructStore(ydoc.store) // exclude the changes from `ydocVersion0` - const insertionSetDiff = Y.diffIdSet(insertionSet, Y.createInsertionSetFromStructStore(ydocVersion0.store)) + const insertionSetDiff = Y.diffIdSet(insertionSet, Y.createInsertionSetFromStructStore(ydocVersion0.store, false)) const deleteSetDiff = Y.diffIdSet(deleteSet, Y.createDeleteSetFromStructStore(ydocVersion0.store)) // assign attributes to the diff const attributedInsertions = createIdMapFromIdSet(insertionSetDiff, [new Y.Attribution('insert', 'Bob')]) diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 121e289be..74b563588 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -380,9 +380,11 @@ export const testElementAttributedContentViaDiffer = _tc => { */ export const testAttributionManagerSimpleExample = _tc => { const ydoc = new Y.Doc() + ydoc.clientID = 0 // create some initial content ydoc.getXmlFragment().insert(0, [new Y.XmlText('hello world')]) const ydocFork = new Y.Doc() + ydocFork.clientID = 1 Y.applyUpdate(ydocFork, Y.encodeStateAsUpdate(ydoc)) // modify the fork // append a span element @@ -390,6 +392,8 @@ export const testAttributionManagerSimpleExample = _tc => { const ytext = /** @type {Y.XmlText} */ (ydocFork.getXmlFragment().get(0)) // make "hello" italic ytext.format(0, 5, { italic: true }) + ytext.insert(11, 'deleteme') + ytext.delete(11, 8) ytext.insert(11, '!') // highlight the changes console.log(JSON.stringify(ydocFork.getXmlFragment().getContentDeep(Y.createAttributionManagerFromDiff(ydoc, ydocFork)), null, 2)) From 7528541713c703565a806d4906915a55b92e98d2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 1 May 2025 15:26:26 +0200 Subject: [PATCH 299/362] bump y-protocols --- package-lock.json | 5939 +++++++++++++++++++++++++++++++++++++++++ package.json | 9 +- rollup.config.js | 4 +- tests/testHelper.js | 2 +- tests/y-text.tests.js | 2 +- 5 files changed, 5947 insertions(+), 9 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..607a9d6cf --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5939 @@ +{ + "name": "yjs", + "version": "14.0.0-2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "yjs", + "version": "14.0.0-2", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.105" + }, + "devDependencies": { + "@types/node": "^22.14.1", + "@y/protocols": "^1.0.6-1", + "concurrently": "^3.6.1", + "jsdoc": "^3.6.7", + "markdownlint-cli": "^0.41.0", + "rollup": "^4.37.0", + "standard": "^16.0.4", + "tui-jsdoc-template": "^1.2.2", + "typescript": "^5.8.3", + "yjs": "." + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", + "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@y/protocols": { + "version": "1.0.6-1", + "resolved": "https://registry.npmjs.org/@y/protocols/-/protocols-1.0.6-1.tgz", + "integrity": "sha512-6hyVR4Azg+JVqeyCkPQMsg9BMpB7fgAldsIDwb5EqJTPLXkQuk/mqK/j0rvIZUuPvJjlYSDBIOQWNsy92iXQsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lib0": "^0.2.85" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^14.0.0-1 || ^14 || ^13" + } + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cheerio/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", + "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.x" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.1.tgz", + "integrity": "sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "commander": "2.6.0", + "date-fns": "^1.23.0", + "lodash": "^4.5.1", + "read-pkg": "^3.0.0", + "rx": "2.3.24", + "spawn-command": "^0.0.2-1", + "supports-color": "^3.2.3", + "tree-kill": "^1.1.0" + }, + "bin": { + "concurrent": "src/main.js", + "concurrently": "src/main.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", + "dev": true, + "license": "BSD-like", + "dependencies": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "node_modules/css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/enquirer/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/enquirer/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.3.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peerDependencies": { + "eslint": "^7.12.1", + "eslint-plugin-import": "^2.22.1", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.2.1 || ^5.0.0" + } + }, + "node_modules/eslint-config-standard-jsx": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", + "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peerDependencies": { + "eslint": "^7.12.1", + "eslint-plugin-react": "^7.21.5" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.3", + "array.prototype.flat": "^1.2.4", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.6.2", + "find-up": "^2.0.0", + "has": "^1.0.3", + "is-core-module": "^2.6.0", + "minimatch": "^3.0.4", + "object.values": "^1.1.4", + "pkg-up": "^2.0.0", + "read-pkg-up": "^3.0.0", + "resolve": "^1.20.0", + "tsconfig-paths": "^3.11.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-node/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-promise": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", + "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", + "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.3", + "array.prototype.flatmap": "^1.2.4", + "doctrine": "^2.1.0", + "estraverse": "^5.2.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.0.4", + "object.entries": "^1.1.4", + "object.fromentries": "^2.0.4", + "object.hasown": "^1.0.0", + "object.values": "^1.1.4", + "prop-types": "^15.7.2", + "resolve": "^2.0.0-next.3", + "string.prototype.matchall": "^4.0.5" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "3.6.11", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", + "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.9.4", + "@types/markdown-it": "^12.2.3", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^12.3.2", + "markdown-it-anchor": "^8.4.1", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lib0": { + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", + "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdownlint": { + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz", + "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "markdown-it": "14.1.0", + "markdownlint-micromark": "0.1.9" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint-cli": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz", + "integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "~12.1.0", + "get-stdin": "~9.0.0", + "glob": "~10.4.1", + "ignore": "~5.3.1", + "js-yaml": "^4.1.0", + "jsonc-parser": "~3.2.1", + "jsonpointer": "5.0.1", + "markdownlint": "~0.34.0", + "minimatch": "~9.0.4", + "run-con": "~1.3.2", + "smol-toml": "~1.2.0" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/markdownlint-micromark": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz", + "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } + }, + "node_modules/markdownlint/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/markdownlint/node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/markdownlint/node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdownlint/node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdownlint/node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "~1.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-conf/node_modules/type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" + }, + "bin": { + "run-con": "cli.js" + } + }, + "node_modules/rx": { + "version": "2.3.24", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", + "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", + "dev": true + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/smol-toml": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.2.tgz", + "integrity": "sha512-fVEjX2ybKdJKzFL46VshQbj9PuA4IUKivalgp48/3zwS9vXzyykzQ6AX92UxHSvWJagziMRLeHMgEzoGO7A8hQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/standard": { + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", + "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "eslint": "~7.18.0", + "eslint-config-standard": "16.0.3", + "eslint-config-standard-jsx": "10.0.0", + "eslint-plugin-import": "~2.24.2", + "eslint-plugin-node": "~11.1.0", + "eslint-plugin-promise": "~5.1.0", + "eslint-plugin-react": "~7.25.1", + "standard-engine": "^14.0.1" + }, + "bin": { + "standard": "bin/cmd.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/standard-engine": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", + "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "get-stdin": "^8.0.0", + "minimist": "^1.2.5", + "pkg-conf": "^3.1.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8.10" + } + }, + "node_modules/standard-engine/node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tui-jsdoc-template": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tui-jsdoc-template/-/tui-jsdoc-template-1.2.2.tgz", + "integrity": "sha512-oqw0IYaot86VJ2owKBozJnilgta0Z55x8r9PeHj7vb+jDoSvJGRUQUcgs56SZh9HE20fx54Pe75p84X85/ygLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio": "^0.22.0" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yjs": { + "resolved": "", + "link": true + } + } +} diff --git a/package.json b/package.json index 2b26567b4..c21f2cedd 100644 --- a/package.json +++ b/package.json @@ -48,12 +48,11 @@ "./package.json": "./package.json" }, "files": [ - "dist/yjs.*", + "dist/*", "dist/src", "src", "tests/testHelper.js", - "dist/testHelper.mjs", - "sponsor-y.js" + "dist/testHelper.mjs" ], "dictionaries": { "test": "tests" @@ -86,10 +85,10 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.105", - "y-protocols": "^1.0.5" + "lib0": "^0.2.105" }, "devDependencies": { + "@y/protocols": "^1.0.6-1", "@types/node": "^22.14.1", "concurrently": "^3.6.1", "jsdoc": "^3.6.7", diff --git a/rollup.config.js b/rollup.config.js index 7939c8246..22d1c612f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -11,7 +11,7 @@ export default [{ entryFileNames: '[name].cjs', sourcemap: true }, - external: id => /^(lib0|y-protocols)\//.test(id) + external: id => /^(lib0|@y)\//.test(id) }, { // esm output input: { @@ -25,5 +25,5 @@ export default [{ entryFileNames: '[name].mjs', sourcemap: true }, - external: id => /^(lib0|y-protocols)\//.test(id) + external: id => /^(lib0|@y)\//.test(id) }] diff --git a/tests/testHelper.js b/tests/testHelper.js index 14b6b949e..d891df2e5 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -2,7 +2,7 @@ import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' -import * as syncProtocol from 'y-protocols/sync' +import * as syncProtocol from '@y/protocols/sync' import * as object from 'lib0/object' import * as map from 'lib0/map' import * as Y from '../src/index.js' diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 50c5f6e60..13bdd8946 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2613,7 +2613,7 @@ const checkResult = result => { * @param {t.TestCase} tc */ export const testAttributionManagerDefaultPerformance = tc => { - const N = 100000 + const N = 10000 const MaxDeletionLength = 5 // 25% chance of deletion const MaxInsertionLength = 5 const ydoc = new Y.Doc() From 397f24bc7dea1a2bd47e20510a731e9dcdf1ecaf Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 1 May 2025 15:31:19 +0200 Subject: [PATCH 300/362] 14.0.0-3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 607a9d6cf..0322662a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-2", + "version": "14.0.0-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-2", + "version": "14.0.0-3", "license": "MIT", "dependencies": { "lib0": "^0.2.105" diff --git a/package.json b/package.json index c21f2cedd..b8c6f62f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-2", + "version": "14.0.0-3", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2aba7bf0fd7bfb7aafb9ad16c434c183c8d63114 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 1 May 2025 15:46:28 +0200 Subject: [PATCH 301/362] add missing exports for y-prosemirror --- src/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 4dfa0b3c2..1aebeb814 100644 --- a/src/index.js +++ b/src/index.js @@ -113,7 +113,9 @@ export { createIdMapFromIdSet, TwosetAttributionManager, noAttributionsManager, - createAttributionManagerFromDiff + iterateStructsByIdSet, + createAttributionManagerFromDiff, + createIdSet } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' From 6e98b3f1f3026f8124837ffe602d713cd2051e8e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 1 May 2025 15:47:54 +0200 Subject: [PATCH 302/362] 14.0.0-4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0322662a5..2b8e2d555 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-3", + "version": "14.0.0-4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-3", + "version": "14.0.0-4", "license": "MIT", "dependencies": { "lib0": "^0.2.105" diff --git a/package.json b/package.json index b8c6f62f0..968c7b362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-3", + "version": "14.0.0-4", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 00c2646031955a0f6ab9ac3824bc53a6d1b6b2c9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 1 May 2025 16:26:23 +0200 Subject: [PATCH 303/362] [idmap/idset] implement forEach and perf improvement on diffingAttributionManager --- src/utils/AttributionManager.js | 16 ++++++++++++++-- src/utils/IdMap.js | 11 +++++++++++ src/utils/IdSet.js | 11 +++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index f363d98ff..869110ab2 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -175,8 +175,20 @@ export class DiffAttributionManager { this._prevBOH = prevDoc.on('beforeObserverCalls', tr => { insertIntoIdSet(_prevDocInserts, tr.insertSet) insertIntoIdSet(prevDocDeletes, tr.deleteSet) - this.inserts = diffIdMap(this.inserts, tr.insertSet) - this.deletes = diffIdMap(this.deletes, tr.deleteSet) + if (tr.insertSet.clients.size < 2) { + tr.insertSet.forEach((idrange, client) => { + this.inserts.delete(client, idrange.clock, idrange.len) + }) + } else { + this.inserts = diffIdMap(this.inserts, tr.insertSet) + } + if (tr.deleteSet.clients.size < 2) { + tr.deleteSet.forEach((attrRange, client) => { + this.deletes.delete(client, attrRange.clock, attrRange.len) + }) + } else { + this.deletes = diffIdMap(this.deletes, tr.deleteSet) + } // @todo fire update ranges on `tr.insertSet` and `tr.deleteSet` }) this._destroyHandler = nextDoc.on('destroy', this.destroy.bind(this)) diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index 9b1687174..f8c684920 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -321,6 +321,17 @@ export class IdMap { this.attrs = new Set() } + /** + * @param {(attrRange:AttrRange, client:number) => void} f + */ + forEach (f) { + this.clients.forEach((ranges, client) => { + ranges.getIds().forEach((range) => { + f(range, client) + }) + }) + } + /** * @param {ID} id * @return {boolean} diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index d9f06d814..5e236b738 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -113,6 +113,17 @@ export class IdSet { this.clients = new Map() } + /** + * @param {(idrange:IdRange, client:number) => void} f + */ + forEach (f) { + this.clients.forEach((ranges, client) => { + ranges.getIds().forEach((range) => { + f(range, client) + }) + }) + } + /** * @param {ID} id * @return {boolean} From 04c6fbde52be10194f72d3091be868c1d97d1fec Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 5 May 2025 14:17:45 +0200 Subject: [PATCH 304/362] fix nicks test case --- attributing-content.md | 12 ++++++------ src/types/AbstractType.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/attributing-content.md b/attributing-content.md index 78040133c..5ed180b8e 100644 --- a/attributing-content.md +++ b/attributing-content.md @@ -67,27 +67,27 @@ const output = [ "attributes": { "italic": true }, - "attribution": { + "attribution": { // no "insert" attribution: the insertion "Hell" is not attributed to anyone "attributes": { - "italic": [ + "italic": [ // the attribute "italic" was added by Bob "Bob" ] } } }, { - "insert": "o " + "insert": "o " // the insertion "o " has no attributions }, { "insert": "World", - "attribution": { + "attribution": { // the insertion "World" was deleted by Bob "delete": [ "Bob" ] } }, { - "insert": "attributions", + "insert": "attributions", // the insertion "attributions" was inserted by Bob "attribution": { "insert": [ "Bob" @@ -95,7 +95,7 @@ const output = [ } }, { - "insert": "!" + "insert": "!" // the insertion "!" has no attributions } ] ``` diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 7f4161d71..a577770fe 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -1021,7 +1021,7 @@ export const typeMapGetContent = (parent, am) => { const tmpcs = [] am.readContent(tmpcs, prevItem) cs = tmpcs.concat(cs) - if (cs[0].attrs == null) { + if (cs.length === 0 || cs[0].attrs == null) { cs.splice(0, cs.findIndex(c => c.attrs != null)) break } From de12a0ff9f3a4c0d42e52ee2f3976ab3440b3722 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 5 May 2025 14:19:26 +0200 Subject: [PATCH 305/362] 14.0.0-5 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b8e2d555..0f5f5c61e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-4", + "version": "14.0.0-5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-4", + "version": "14.0.0-5", "license": "MIT", "dependencies": { "lib0": "^0.2.105" diff --git a/package.json b/package.json index 968c7b362..ee7423361 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-4", + "version": "14.0.0-5", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 0efa4dd2a7585ad8f16cd385ae6021d019cfd2ef Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 7 May 2025 00:35:57 +0200 Subject: [PATCH 306/362] [diffing] event returns delta class object, migrate away from legacy deltas, work on snapshots using attribution manager. WIP --- package-lock.json | 283 +--------------------- package.json | 4 +- src/types/AbstractType.js | 2 + src/types/YArray.js | 4 +- src/types/YText.js | 357 ++++++--------------------- src/types/YXmlElement.js | 4 +- src/types/YXmlFragment.js | 4 +- src/types/YXmlText.js | 60 ++--- src/utils/AttributionManager.js | 70 +++++- src/utils/Delta.js | 269 ++++++++++++++++----- src/utils/YEvent.js | 14 +- test.html | 22 +- tests/compatibility.tests.js | 14 +- tests/delta.tests.js | 58 ++++- tests/doc.tests.js | 25 -- tests/testHelper.js | 4 +- tests/undo-redo.tests.js | 20 +- tests/updates.tests.js | 4 +- tests/y-text.tests.js | 413 ++++++++++++++------------------ tests/y-xml.tests.js | 6 +- 20 files changed, 677 insertions(+), 960 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f5f5c61e..cab1792a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "14.0.0-5", "license": "MIT", "dependencies": { - "lib0": "^0.2.105" + "lib0": "^0.2.107" }, "devDependencies": { "@types/node": "^22.14.1", @@ -206,216 +206,6 @@ "node": ">=14" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", - "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", - "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", - "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", - "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", - "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", - "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", - "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", - "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", - "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", - "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", - "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", - "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", - "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", - "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", - "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.45.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", @@ -430,62 +220,6 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", - "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", - "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", - "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", - "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2338,21 +2072,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", diff --git a/package.json b/package.json index ee7423361..f0d4a1905 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "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" + "gentesthtml": "0gentesthtml --script ./tests/index.js --include-dependencies @y/protocols > test.html" }, "exports": { ".": { @@ -85,7 +85,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.105" + "lib0": "^0.2.107" }, "devDependencies": { "@y/protocols": "^1.0.6-1", diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index a577770fe..b9b1b44f2 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -728,6 +728,8 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem, case Boolean: case Array: case String: + case BigInt: + case Date: jsonContent.push(c) break default: diff --git a/src/types/YArray.js b/src/types/YArray.js index 086ac0017..5f996755e 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -225,8 +225,8 @@ export class YArray extends AbstractType { */ getContentDeep (am = noAttributionsManager) { return this.getContent(am).map(d => /** @type {any} */ ( - d instanceof delta.InsertOp && d.insert instanceof Array - ? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) + d instanceof delta.InsertArrayOp && d.insert instanceof Array + ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) : d )) } diff --git a/src/types/YText.js b/src/types/YText.js index 6e08563f9..059e3284d 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -7,7 +7,6 @@ import { AbstractType, getItemCleanStart, getState, - isVisible, createID, YTextRefID, callTypeObservers, @@ -16,7 +15,6 @@ import { GC, ContentFormat, ContentString, - splitSnapshotAffectedStructs, iterateStructsByIdSet, findMarker, typeMapDelete, @@ -26,7 +24,7 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ID, Doc, Item, Snapshot, Transaction, // eslint-disable-line + noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line createAttributionFromAttributionItems } from '../internals.js' @@ -622,12 +620,12 @@ export class YTextEvent extends YEvent { } /** - * @type {{added:Set,deleted:Set,keys:Map,delta:Array<{insert?:Array|string, delete?:number, retain?:number}>}} + * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} */ get changes () { if (this._changes === null) { /** - * @type {{added:Set,deleted:Set,keys:Map,delta:Array<{insert?:Array|string|AbstractType|object, delete?:number, retain?:number}>}} + * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} */ const changes = { keys: this.keys, @@ -644,192 +642,106 @@ export class YTextEvent extends YEvent { * Compute the changes in the delta format. * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. * - * @type {Array<{insert?:string|object|AbstractType, delete?:number, retain?:number, attributes?: Object}>} + * @type {delta.TextDelta} * * @public */ get delta () { if (this._delta === null) { - const y = /** @type {Doc} */ (this.target.doc) - /** - * @type {Array<{insert?:string|object|AbstractType, delete?:number, retain?:number, attributes?: Object}>} - */ - const delta = [] - transact(y, transaction => { - const currentAttributes = new Map() // saves all current attributes for insert - const oldAttributes = new Map() - let item = this.target._start + const ydoc = /** @type {Doc} */ (this.target.doc) + const d = this._delta = delta.createTextDelta() + transact(ydoc, transaction => { /** - * @type {string?} + * @type {import('../utils/Delta.js').FormattingAttributes} */ - let action = null + let currentAttributes = {} // saves all current attributes for insert + let usingCurrentAttributes = false /** - * @type {Object} + * @type {import('../utils/Delta.js').FormattingAttributes} */ - const attributes = {} // counts added or removed new attributes for retain + let changedAttributes = {} // saves changed attributes for retain + let usingChangedAttributes = false /** - * @type {string|object} + * @type {import('../utils/Delta.js').FormattingAttributes} */ - let insert = '' - let retain = 0 - let deleteLen = 0 - const addOp = () => { - if (action !== null) { - /** - * @type {any} - */ - let op = null - switch (action) { - case 'delete': - if (deleteLen > 0) { - op = { delete: deleteLen } - } - deleteLen = 0 - break - case 'insert': - if (typeof insert === 'object' || insert.length > 0) { - op = { insert } - if (currentAttributes.size > 0) { - op.attributes = {} - currentAttributes.forEach((value, key) => { - if (value !== null) { - op.attributes[key] = value - } - }) - } - } - insert = '' - break - case 'retain': - if (retain > 0) { - op = { retain } - if (!object.isEmpty(attributes)) { - op.attributes = object.assign({}, attributes) - } - } - retain = 0 - break - } - if (op) delta.push(op) - action = null - } - } + const previousAttributes = {} // The value before changes + const tr = this.transaction + let item = this.target._start while (item !== null) { + const freshDelete = item.deleted && tr.deleteSet.hasId(item.id) && !tr.insertSet.hasId(item.id) + const freshInsert = !item.deleted && tr.insertSet.hasId(item.id) switch (item.content.constructor) { case ContentType: case ContentEmbed: - if (this.adds(item)) { - if (!this.deletes(item)) { - addOp() - action = 'insert' - insert = item.content.getContent()[0] - addOp() - } - } else if (this.deletes(item)) { - if (action !== 'delete') { - addOp() - action = 'delete' - } - deleteLen += 1 + if (freshInsert) { + d.usedAttributes = currentAttributes + usingCurrentAttributes = true + d.insert(item.content.getContent()[0]) + } else if (freshDelete) { + d.delete(1) } else if (!item.deleted) { - if (action !== 'retain') { - addOp() - action = 'retain' - } - retain += 1 + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(1) } break case ContentString: - if (this.adds(item)) { - if (!this.deletes(item)) { - if (action !== 'insert') { - addOp() - action = 'insert' - } - insert += /** @type {ContentString} */ (item.content).str - } - } else if (this.deletes(item)) { - if (action !== 'delete') { - addOp() - action = 'delete' - } - deleteLen += item.length + if (freshInsert) { + d.usedAttributes = currentAttributes + usingCurrentAttributes = true + d.insert(/** @type {ContentString} */ (item.content).str) + } else if (freshDelete) { + d.delete(item.length) } else if (!item.deleted) { - if (action !== 'retain') { - addOp() - action = 'retain' - } - retain += item.length + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(item.length) } break case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (item.content) - if (this.adds(item)) { - if (!this.deletes(item)) { - const curVal = currentAttributes.get(key) ?? null - if (!equalAttrs(curVal, value)) { - if (action === 'retain') { - addOp() - } - if (equalAttrs(value, (oldAttributes.get(key) ?? null))) { - delete attributes[key] - } else { - attributes[key] = value - } - } else if (value !== null) { - item.delete(transaction) - } + const currAttrVal = currentAttributes[key] ?? null + if (freshDelete || freshInsert) { + // create fresh references + if (usingCurrentAttributes) { + currentAttributes = object.assign({}, currentAttributes) + usingCurrentAttributes = false } - } else if (this.deletes(item)) { - oldAttributes.set(key, value) - const curVal = currentAttributes.get(key) ?? null - if (!equalAttrs(curVal, value)) { - if (action === 'retain') { - addOp() - } - attributes[key] = curVal - } - } else if (!item.deleted) { - oldAttributes.set(key, value) - const attr = attributes[key] - if (attr !== undefined) { - if (!equalAttrs(attr, value)) { - if (action === 'retain') { - addOp() - } - if (value === null) { - delete attributes[key] - } else { - attributes[key] = value - } - } else if (attr !== null) { // this will be cleaned up automatically by the contextless cleanup function - item.delete(transaction) - } + if (usingChangedAttributes) { + usingChangedAttributes = false + changedAttributes = object.assign({}, changedAttributes) } } - if (!item.deleted) { - if (action === 'insert') { - addOp() + if (freshInsert) { + if (equalAttrs(value, currAttrVal)) { + item.delete(transaction) + } else if (equalAttrs(value, previousAttributes[key] ?? null)) { + delete currentAttributes[key] + delete changedAttributes[key] + } else { + currentAttributes[key] = value + changedAttributes[key] = value } - updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (item.content)) + } else if (freshDelete) { + changedAttributes[key] = currAttrVal + currentAttributes[key] = currAttrVal + previousAttributes[key] = value + } else if (!item.deleted) { + // fresh reference to currentAttributes only + if (usingCurrentAttributes) { + currentAttributes = object.assign({}, currentAttributes) + usingCurrentAttributes = false + } + currentAttributes[key] = value + previousAttributes[key] = value } break } } item = item.right } - addOp() - while (delta.length > 0) { - const lastOp = delta[delta.length - 1] - if (lastOp.retain !== undefined && lastOp.attributes === undefined) { - // retain delta's if they don't assign attributes - delta.pop() - } else { - break - } - } }) - this._delta = delta + d.done() } return /** @type {any} */ (this._delta) } @@ -903,7 +815,7 @@ export class YText extends AbstractType { */ clone () { const text = new YText() - text.applyDelta(this.toDelta()) + text.applyDelta(this.getContent()) return text } @@ -957,7 +869,7 @@ export class YText extends AbstractType { /** * Apply a {@link Delta} on this shared YText type. * - * @param {Array} delta The changes to apply on this element. + * @param {Array | delta.Delta} delta The changes to apply on this element. * @param {object} opts * @param {boolean} [opts.sanitize] Sanitize input delta. Removes ending newlines if set to true. * @@ -967,16 +879,20 @@ export class YText extends AbstractType { applyDelta (delta, { sanitize = true } = {}) { if (this.doc !== null) { transact(this.doc, transaction => { + /** + * @type {Array} + */ + const deltaOps = /** @type {Array} */ (/** @type {delta.Delta} */ (delta).ops instanceof Array ? /** @type {delta.Delta} */ (delta).ops : delta) const currPos = new ItemTextListPosition(null, this._start, 0, new Map()) - for (let i = 0; i < delta.length; i++) { - const op = delta[i] + for (let i = 0; i < deltaOps.length; i++) { + const op = deltaOps[i] if (op.insert !== undefined) { // Quill assumes that the content starts with an empty paragraph. // Yjs/Y.Text assumes that it starts empty. We always hide that // there is a newline at the end of the content. // If we omit this step, clients will see a different number of // paragraphs, but nothing bad will happen. - const ins = (!sanitize && typeof op.insert === 'string' && i === delta.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert + const ins = (!sanitize && typeof op.insert === 'string' && i === deltaOps.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert if (typeof ins !== 'string' || ins.length > 0) { insertText(transaction, this, currPos, ins, op.attributes || {}) } @@ -1006,8 +922,8 @@ export class YText extends AbstractType { */ getContentDeep (am = noAttributionsManager) { return this.getContent(am).map(d => - d instanceof delta.InsertOp && d.insert instanceof AbstractType - ? new delta.InsertOp(d.insert.getContent(am), d.attributes, d.attribution) + d instanceof delta.InsertStringOp && d.insert instanceof AbstractType + ? new delta.InsertStringOp(d.insert.getContent(am), d.attributes, d.attribution) : d ) } @@ -1091,121 +1007,6 @@ export class YText extends AbstractType { return d } - /** - * Returns the Delta representation of this YText type. - * - * @param {Snapshot} [snapshot] - * @param {Snapshot} [prevSnapshot] - * @param {function('removed' | 'added', ID):any} [computeYChange] - * @return {any} The Delta representation of this type. - * - * @public - */ - toDelta (snapshot, prevSnapshot, computeYChange) { - this.doc ?? warnPrematureAccess() - /** - * @type{Array} - */ - const ops = [] - const currentAttributes = new Map() - const doc = /** @type {Doc} */ (this.doc) - let str = '' - let n = this._start - function packStr () { - if (str.length > 0) { - // pack str with attributes to ops - /** - * @type {Object} - */ - const attributes = {} - let addAttributes = false - currentAttributes.forEach((value, key) => { - addAttributes = true - attributes[key] = value - }) - /** - * @type {Object} - */ - const op = { insert: str } - if (addAttributes) { - op.attributes = attributes - } - ops.push(op) - str = '' - } - } - const computeDelta = () => { - while (n !== null) { - if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) { - switch (n.content.constructor) { - case ContentString: { - const cur = currentAttributes.get('ychange') - if (snapshot !== undefined && !isVisible(n, snapshot)) { - if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') { - packStr() - currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' }) - } - } else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) { - if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') { - packStr() - currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' }) - } - } else if (cur !== undefined) { - packStr() - currentAttributes.delete('ychange') - } - str += /** @type {ContentString} */ (n.content).str - break - } - case ContentType: - case ContentEmbed: { - packStr() - /** - * @type {Object} - */ - const op = { - insert: n.content.getContent()[0] - } - if (currentAttributes.size > 0) { - const attrs = /** @type {Object} */ ({}) - op.attributes = attrs - currentAttributes.forEach((value, key) => { - attrs[key] = value - }) - } - ops.push(op) - break - } - case ContentFormat: - if (isVisible(n, snapshot)) { - packStr() - updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content)) - } - break - } - } - n = n.right - } - packStr() - } - if (snapshot || prevSnapshot) { - // snapshots are merged again after the transaction, so we need to keep the - // transaction alive until we are done - transact(doc, transaction => { - if (snapshot) { - splitSnapshotAffectedStructs(transaction, snapshot) - } - if (prevSnapshot) { - splitSnapshotAffectedStructs(transaction, prevSnapshot) - } - computeDelta() - }, 'cleanup') - } else { - computeDelta() - } - return ops - } - /** * Insert text at a given index. * diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 25e03aa65..8d3527cd0 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -225,8 +225,8 @@ export class YXmlElement extends YXmlFragment { getContentDeep (am = noAttributionsManager) { const { children: origChildren, attributes: origAttributes } = this.getContent(am) const children = origChildren.map(d => /** @type {any} */ ( - (d instanceof delta.InsertOp && d.insert instanceof Array) - ? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) + (d instanceof delta.InsertArrayOp && d.insert instanceof Array) + ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) : d )) /** diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 3d33c9da5..804d442c5 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -403,8 +403,8 @@ export class YXmlFragment extends AbstractType { * @type {import('../utils/Delta.js').ArrayDelta>} */ const children = origChildren.map(d => /** @type {any} */ ( - d instanceof delta.InsertOp && d.insert instanceof Array - ? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) + d instanceof delta.InsertArrayOp && d.insert instanceof Array + ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) : d )) return { children } diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index ab02dbf3e..7b5ac37c3 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -4,6 +4,8 @@ import { ContentType, YXmlElement, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, // eslint-disable-line } from '../internals.js' +import * as delta from '../utils/Delta.js' + /** * Represents text in a Dom Element. In the future this type will also handle * simple formatting information like bold and italic. @@ -38,7 +40,7 @@ export class YXmlText extends YText { */ clone () { const text = new YXmlText() - text.applyDelta(this.toDelta()) + text.applyDelta(this.getContent()) return text } @@ -66,36 +68,38 @@ export class YXmlText extends YText { } toString () { - // @ts-ignore - return this.toDelta().map(delta => { - const nestedNodes = [] - for (const nodeName in delta.attributes) { - const attrs = [] - for (const key in delta.attributes[nodeName]) { - attrs.push({ key, value: delta.attributes[nodeName][key] }) + return this.getContent().ops.map(dop => { + if (dop instanceof delta.InsertStringOp) { + const nestedNodes = [] + for (const nodeName in dop.attributes) { + const attrs = [] + for (const key in dop.attributes[nodeName]) { + attrs.push({ key, value: dop.attributes[nodeName][key] }) + } + // sort attributes to get a unique order + attrs.sort((a, b) => a.key < b.key ? -1 : 1) + nestedNodes.push({ nodeName, attrs }) } - // sort attributes to get a unique order - attrs.sort((a, b) => a.key < b.key ? -1 : 1) - nestedNodes.push({ nodeName, attrs }) - } - // sort node order to get a unique order - nestedNodes.sort((a, b) => a.nodeName < b.nodeName ? -1 : 1) - // now convert to dom string - let str = '' - for (let i = 0; i < nestedNodes.length; i++) { - const node = nestedNodes[i] - str += `<${node.nodeName}` - for (let j = 0; j < node.attrs.length; j++) { - const attr = node.attrs[j] - str += ` ${attr.key}="${attr.value}"` + // sort node order to get a unique order + nestedNodes.sort((a, b) => a.nodeName < b.nodeName ? -1 : 1) + // now convert to dom string + let str = '' + for (let i = 0; i < nestedNodes.length; i++) { + const node = nestedNodes[i] + str += `<${node.nodeName}` + for (let j = 0; j < node.attrs.length; j++) { + const attr = node.attrs[j] + str += ` ${attr.key}="${attr.value}"` + } + str += '>' } - str += '>' - } - str += delta.insert - for (let i = nestedNodes.length - 1; i >= 0; i--) { - str += `` + str += dop.insert + for (let i = nestedNodes.length - 1; i >= 0; i--) { + str += `` + } + return str } - return str + return '' }).join('') } diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 869110ab2..48104a462 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -5,10 +5,14 @@ import { createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, - Doc, Item, AbstractContent, IdMap, // eslint-disable-line + Snapshot, Doc, Item, AbstractContent, IdMap, // eslint-disable-line insertIntoIdMap, insertIntoIdSet, - diffIdMap + diffIdMap, + createIdMap, + createAttributionItem, + mergeIdMaps, + AttributionItem } from '../internals.js' import * as error from 'lib0/error' @@ -90,6 +94,8 @@ export class AbstractAttributionManager { readContent (_contents, _item) { error.methodUnimplemented() } + + destroy () {} } /** @@ -105,6 +111,8 @@ export class TwosetAttributionManager { this.deletes = deletes } + destroy () {} + /** * @param {Array>} contents * @param {Item} item @@ -131,6 +139,8 @@ export class TwosetAttributionManager { * @implements AbstractAttributionManager */ export class NoAttributionsManager { + destroy () {} + /** * @param {Array>} contents * @param {Item} item @@ -243,3 +253,59 @@ export class DiffAttributionManager { * @param {Doc} nextDoc */ export const createAttributionManagerFromDiff = (prevDoc, nextDoc) => new DiffAttributionManager(prevDoc, nextDoc) + +/** + * Intended for projects that used the v13 snapshot feature. With this AttributionManager you can + * read content similar to the previous snapshot api. Requires that `ydoc.gc` is turned off. + * + * @implements AbstractAttributionManager + */ +export class SnapshotAttributionManager { + /** + * @param {Snapshot} prevSnapshot + * @param {Snapshot} nextSnapshot + */ + constructor (prevSnapshot, nextSnapshot) { + this.prevSnapshot = prevSnapshot + this.nextSnapshot = nextSnapshot + const inserts = createIdMap() + const deletes = createIdMapFromIdSet(diffIdSet(nextSnapshot.ds, prevSnapshot.ds), [createAttributionItem('change', '')]) + nextSnapshot.sv.forEach((clock, client) => { + inserts.add(client, 0, prevSnapshot.sv.get(client) || 0, []) + inserts.add(client, prevSnapshot.sv.get(client) || 0, clock, [createAttributionItem('change', '')]) + }) + this.attrs = mergeIdMaps([diffIdMap(inserts, prevSnapshot.ds), deletes]) + } + + destroy () { } + + /** + * @param {Array>} contents + * @param {Item} item + */ + readContent (contents, item) { + if ((this.nextSnapshot.sv.get(item.id.client) ?? 0) <= item.id.clock) return // future item that should not be displayed + const slice = this.attrs.slice(item.id, item.length) + let content = slice.length === 1 ? item.content : item.content.copy() + slice.forEach(s => { + const deleted = this.nextSnapshot.ds.has(item.id.client, s.clock) + const c = content + if (s.len < c.getLength()) { + content = c.splice(s.len) + } + if (!deleted || (s.attrs != null && s.attrs.length > 0)) { + let attrsWithoutChange = s.attrs?.filter(attr => attr.name !== 'change') ?? null + if (s.attrs?.length === 0) { + attrsWithoutChange = null + } + contents.push(new AttributedContent(c, deleted, attrsWithoutChange)) + } + }) + } +} + +/** + * @param {Snapshot} prevSnapshot + * @param {Snapshot} nextSnapshot + */ +export const createAttributionManagerFromSnapshots = (prevSnapshot, nextSnapshot = prevSnapshot) => new SnapshotAttributionManager(prevSnapshot, nextSnapshot) diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 22102614e..dace41f7b 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -1,10 +1,22 @@ import * as object from 'lib0/object' import * as fun from 'lib0/function' import * as traits from 'lib0/traits' +import * as error from 'lib0/error' /** - * @template {string|Array|{[key: string]: any}} Content - * @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp + * @template {any} ArrayContent + * @template {{[key: string]: any}} Embeds + * @typedef {InsertStringOp|InsertEmbedOp|InsertArrayOp|RetainOp|DeleteOp} DeltaOp + */ + +/** + * @template {{[key: string]: any}} Embeds + * @typedef {InsertStringOp|InsertEmbedOp|RetainOp|DeleteOp} TextDeltaOp + */ + +/** + * @template {any} ArrayContent + * @typedef {InsertArrayOp|RetainOp|DeleteOp} ArrayDeltaOp */ /** @@ -16,11 +28,16 @@ import * as traits from 'lib0/traits' */ /** - * @template {string|Array|{[key: string]: any}} Content + * @typedef {Array} DeltaJson + */ + +/** + * @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution }} DeltaJsonOp */ -export class InsertOp { + +export class InsertStringOp { /** - * @param {Content} insert + * @param {string} insert * @param {FormattingAttributes|null} attributes * @param {Attribution|null} attribution */ @@ -34,12 +51,83 @@ export class InsertOp { return (this.insert.constructor === Array || this.insert.constructor === String) ? this.insert.length : 1 } + /** + * @return {DeltaJsonOp} + */ + toJSON () { + return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) + } + + /** + * @param {InsertStringOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) + } +} + +/** + * @template {any} ArrayContent + */ +export class InsertArrayOp { + /** + * @param {Array} insert + * @param {FormattingAttributes|null} attributes + * @param {Attribution|null} attribution + */ + constructor (insert, attributes, attribution) { + this.insert = insert + this.attributes = attributes + this.attribution = attribution + } + + get length () { + return this.insert.length + } + + /** + * @return {DeltaJsonOp} + */ toJSON () { return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) } /** - * @param {InsertOp} other + * @param {InsertArrayOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) + } +} + +/** + * @template {{[key: string]: any}} Embeds + */ +export class InsertEmbedOp { + /** + * @param {Embeds} insert + * @param {FormattingAttributes|null} attributes + * @param {Attribution|null} attribution + */ + constructor (insert, attributes, attribution) { + this.insert = insert + this.attributes = attributes + this.attribution = attribution + } + + get length () { + return 1 + } + + /** + * @return {DeltaJsonOp} + */ + toJSON () { + return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) + } + + /** + * @param {InsertEmbedOp} other */ [traits.EqualityTraitSymbol] (other) { return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) @@ -58,6 +146,9 @@ export class DeleteOp { return 0 } + /** + * @return {DeltaJsonOp} + */ toJSON () { return { delete: this.delete } } @@ -86,6 +177,9 @@ export class RetainOp { return this.retain } + /** + * @return {DeltaJsonOp} + */ toJSON () { return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {}) } @@ -98,25 +192,17 @@ export class RetainOp { } } -/** - * @typedef {Array} ArrayDeltaContent - */ - /** * @typedef {string | { [key: string]: any }} TextDeltaContent */ /** - * @typedef {{ array: ArrayDeltaContent, text: TextDeltaContent, custom: string|Array|{[key:string]:any}}} DeltaTypeMapper - */ - -/** - * @typedef {(TextDelta | ArrayDelta)} Delta + * @typedef {(TextDelta | ArrayDelta)} Delta */ /** * @template {'array' | 'text' | 'custom'} Type - * @template {DeltaTypeMapper[Type]} [Content=DeltaTypeMapper[Type]] + * @template {DeltaOp} TDeltaOp */ export class AbstractDelta { /** @@ -125,26 +211,26 @@ export class AbstractDelta { constructor (type) { this.type = type /** - * @type {Array>} + * @type {Array} */ this.ops = [] } /** - * @template {DeltaTypeMapper[Type]} MContent - * @param {(d:DeltaOp)=>DeltaOp} f - * @return {DeltaBuilder} + * @template {(d:TDeltaOp) => DeltaOp} Mapper + * @param {Mapper} f + * @return {DeltaBuilder infer OP ? OP : unknown>} */ map (f) { const d = /** @type {DeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) d.ops = this.ops.map(f) // @ts-ignore - d._lastOp = d.ops[d.ops.length - 1] ?? null + d.lastOp = d.ops[d.ops.length - 1] ?? null return d } /** - * @param {(d:DeltaOp,index:number)=>void} f + * @param {(d:TDeltaOp,index:number)=>void} f */ forEach (f) { for ( @@ -157,22 +243,25 @@ export class AbstractDelta { } /** - * @param {AbstractDelta} other + * @param {AbstractDelta} other * @return {boolean} */ equals (other) { return this[traits.EqualityTraitSymbol](other) } + /** + * @returns {DeltaJson} + */ toJSON () { - return { ops: this.ops.map(o => o.toJSON()) } + return this.ops.map(o => o.toJSON()) } /** - * @param {AbstractDelta} other + * @param {AbstractDelta} other */ [traits.EqualityTraitSymbol] (other) { - return this.type === other.type && fun.equalityDeep(this.ops, other.ops) + return fun.equalityDeep(this.ops, other.ops) } } @@ -184,15 +273,14 @@ export class AbstractDelta { * @param {T | null} b */ const mergeAttrs = (a, b) => { - const merged = a == null ? b : (b == null ? a : object.assign({}, a, b)) - if (merged == null || object.isEmpty(merged)) { return null } - return merged + const merged = object.isEmpty(a) ? b : (object.isEmpty(b) ? a : object.assign({}, a, b)) + return object.isEmpty(merged) ? null : merged } /** * @template {'array' | 'text' | 'custom'} [Type='custom'] - * @template {DeltaTypeMapper[Type]} [Content=DeltaTypeMapper[Type]] - * @extends AbstractDelta + * @template {DeltaOp} [TDeltaOp=DeltaOp] + * @extends AbstractDelta */ export class DeltaBuilder extends AbstractDelta { /** @@ -209,10 +297,9 @@ export class DeltaBuilder extends AbstractDelta { */ this.usedAttribution = null /** - * @private - * @type {DeltaOp?} + * @type {TDeltaOp?} */ - this._lastOp = null + this.lastOp = null } /** @@ -220,8 +307,44 @@ export class DeltaBuilder extends AbstractDelta { * @return {this} */ useAttributes (attributes) { - if (this.usedAttributes === attributes) return this - this.usedAttributes = attributes && (object.isEmpty(attributes) ? null : object.assign({}, attributes)) + this.usedAttributes = object.isEmpty(attributes) ? null : object.assign({}, attributes) + return this + } + + /** + * @param {string} name + * @param {any} value + */ + updateUsedAttributes (name, value) { + if (value == null) { + this.usedAttributes = object.assign({}, this.usedAttributes) + delete this.usedAttributes?.[name] + if (object.isEmpty(this.usedAttributes)) { + this.usedAttributes = null + } + } else if (!fun.equalityDeep(this.usedAttributes?.[name], value)) { + this.usedAttributes = object.assign({}, this.usedAttributes) + this.usedAttributes[name] = value + } + return this + } + + /** + * @template {keyof Attribution} NAME + * @param {NAME} name + * @param {Attribution[NAME]?} value + */ + updateUsedAttribution (name, value) { + if (value == null) { + this.usedAttribution = object.assign({}, this.usedAttribution) + delete this.usedAttribution?.[name] + if (object.isEmpty(this.usedAttribution)) { + this.usedAttribution = null + } + } else if (!fun.equalityDeep(this.usedAttribution?.[name], value)) { + this.usedAttribution = object.assign({}, this.usedAttribution) + this.usedAttribution[name] = value + } return this } @@ -229,32 +352,30 @@ export class DeltaBuilder extends AbstractDelta { * @param {Attribution?} attribution */ useAttribution (attribution) { - if (this.usedAttribution === attribution) return this - this.usedAttribution = attribution && (object.isEmpty(attribution) ? null : object.assign({}, attribution)) + this.usedAttribution = object.isEmpty(attribution) ? null : object.assign({}, attribution) return this } /** - * @param {Content} insert + * @param {(TDeltaOp extends TextDelta ? string | Embeds : never) | (TDeltaOp extends InsertArrayOp ? Array : never) } insert * @param {FormattingAttributes?} attributes * @param {Attribution?} attribution * @return {this} */ insert (insert, attributes = null, attribution = null) { - const mergedAttributes = attributes == null ? this.usedAttributes : mergeAttrs(this.usedAttributes, attributes) - const mergedAttribution = attribution == null ? this.usedAttribution : mergeAttrs(this.usedAttribution, attribution) - if (this._lastOp instanceof InsertOp && (mergedAttributes === this._lastOp.attributes || fun.equalityDeep(mergedAttributes, this._lastOp.attributes)) && (mergedAttribution === this._lastOp.attribution || fun.equalityDeep(mergedAttribution, this._lastOp.attribution))) { + const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) + const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) + if (((this.lastOp instanceof InsertStringOp && insert.constructor === String) || (this.lastOp instanceof InsertArrayOp && insert.constructor === Array)) && (mergedAttributes === this.lastOp.attributes || fun.equalityDeep(mergedAttributes, this.lastOp.attributes)) && (mergedAttribution === this.lastOp.attribution || fun.equalityDeep(mergedAttribution, this.lastOp.attribution))) { if (insert.constructor === String) { // @ts-ignore - this._lastOp.insert += insert - } else if (insert.constructor === Array && this._lastOp.insert.constructor === Array) { - // @ts-ignore - this._lastOp.insert.push(...insert) + this.lastOp.insert += insert } else { - this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) + // @ts-ignore + this.lastOp.insert.push(...insert) } } else { - this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution)) + const OpConstructor = /** @type {any} */ (insert.constructor === String ? InsertStringOp : (insert.constructor === Array ? InsertArrayOp : InsertEmbedOp)) + this.ops.push(this.lastOp = new OpConstructor(insert, object.isEmpty(mergedAttributes) ? null : mergedAttributes, object.isEmpty(mergedAttribution) ? null : mergedAttribution)) } return this } @@ -268,10 +389,11 @@ export class DeltaBuilder extends AbstractDelta { retain (retain, attributes = null, attribution = null) { const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) - if (this._lastOp instanceof RetainOp && fun.equalityDeep(mergedAttributes, this._lastOp.attributes) && fun.equalityDeep(mergedAttribution, this._lastOp.attribution)) { - this._lastOp.retain += retain + if (this.lastOp instanceof RetainOp && fun.equalityDeep(mergedAttributes, this.lastOp.attributes) && fun.equalityDeep(mergedAttribution, this.lastOp.attribution)) { + this.lastOp.retain += retain } else { - this.ops.push(this._lastOp = new RetainOp(retain, mergedAttributes, mergedAttribution)) + // @ts-ignore + this.ops.push(this.lastOp = new RetainOp(retain, mergedAttributes, mergedAttribution)) } return this } @@ -281,25 +403,30 @@ export class DeltaBuilder extends AbstractDelta { * @return {this} */ delete (len) { - if (this._lastOp instanceof DeleteOp) { - this._lastOp.delete += len + if (this.lastOp instanceof DeleteOp) { + this.lastOp.delete += len } else { - this.ops.push(this._lastOp = new DeleteOp(len)) + // @ts-ignore + this.ops.push(this.lastOp = new DeleteOp(len)) } return this } /** - * @return {AbstractDelta} + * @return {AbstractDelta} */ done () { + while (this.lastOp != null && this.lastOp instanceof RetainOp && this.lastOp.attributes === null) { + this.ops.pop() + this.lastOp = this.ops[this.ops.length - 1] ?? null + } return this } } /** - * @template {ArrayDeltaContent} [Content=ArrayDeltaContent] - * @extends DeltaBuilder<'array',Content> + * @template {any} ArrayContent + * @extends DeltaBuilder<'array', ArrayDeltaOp>> */ export class ArrayDelta extends DeltaBuilder { constructor () { @@ -308,8 +435,8 @@ export class ArrayDelta extends DeltaBuilder { } /** - * @template {TextDeltaContent} [Content=TextDeltaContent] - * @extends DeltaBuilder<'text',Content> + * @template {{ [key:string]: any }} Embeds + * @extends DeltaBuilder<'text',TextDeltaOp> */ export class TextDelta extends DeltaBuilder { constructor () { @@ -323,6 +450,28 @@ export class TextDelta extends DeltaBuilder { export const createTextDelta = () => new TextDelta() /** - * @return {ArrayDelta} + * @return {ArrayDelta} */ export const createArrayDelta = () => new ArrayDelta() + +/** + * @param {DeltaJson} ops + * @param {'custom' | 'text' | 'array'} type + */ +export const fromJSON = (ops, type = 'custom') => { + const d = new DeltaBuilder(type) + for (let i = 0; i < ops.length; i++) { + const op = /** @type {any} */ (ops[i]) + // @ts-ignore + if (op.insert !== undefined) { + d.insert(op.insert, op.attributes, op.attribution) + } else if (op.retain !== undefined) { + d.retain(op.retain, op.attributes ?? null, op.attribution ?? null) + } else if (op.delete !== undefined) { + d.delete(op.delete) + } else { + error.unexpectedCase() + } + } + return d.done() +} diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index c8f6e3605..b7b8ebe39 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -6,6 +6,14 @@ import * as set from 'lib0/set' import * as array from 'lib0/array' import * as error from 'lib0/error' +/** + * @typedef {import('../utils/Delta.js').TextDelta} TextDelta + */ + +/** + * @typedef {import('../utils/Delta.js').Delta} Delta + */ + const errorComputeChanges = 'You must not compute changes after the event-handler fired.' /** @@ -42,7 +50,7 @@ export class YEvent { */ this._keys = null /** - * @type {null | Array<{ insert?: string | Array | object | AbstractType, retain?: number, delete?: number, attributes?: Object }>} + * @type {TextDelta?} */ this._delta = null /** @@ -142,7 +150,7 @@ export class YEvent { * unexpected behavior (incorrect computation of deltas). A safe way to collect changes * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. * - * @type {Array<{insert?: string | Array | object | AbstractType, retain?: number, delete?: number, attributes?: Object}>} + * @type {Delta} */ get delta () { return this.changes.delta @@ -166,7 +174,7 @@ export class YEvent { * unexpected behavior (incorrect computation of deltas). A safe way to collect changes * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. * - * @type {{added:Set,deleted:Set,keys:Map,delta:Array<{insert?:Array|string, delete?:number, retain?:number}>}} + * @type {{added:Set,deleted:Set,keys:Map,delta:Delta}} */ get changes () { let changes = this._changes diff --git a/test.html b/test.html index 9273884e4..fb01fd9d7 100644 --- a/test.html +++ b/test.html @@ -157,23 +157,23 @@ "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" + "@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/": { + "./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", diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 0b8e11003..1155818e8 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -10,9 +10,9 @@ import * as t from 'lib0/testing' import * as buffer from 'lib0/buffer' /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testArrayCompatibilityV1 = tc => { +export const testArrayCompatibilityV1 = _tc => { const oldDoc = 'BV8EAAcBBWFycmF5AAgABAADfQF9An0DgQQDAYEEAAEABMEDAAQAAccEAAQFASEABAsIc29tZXByb3ACqAQNAX0syAQLBAUBfYoHwQQPBAUBwQQQBAUByAQRBAUBfYoHyAQQBBEBfY0HyAQTBBEBfY0HyAQUBBEBfY0HyAQVBBEBfY0HyAQQBBMBfY4HyAQXBBMBfY4HwQQYBBMBxwQXBBgACAAEGgR9AX0CfQN9BMEBAAEBAQADxwQLBA8BIQAEIwhzb21lcHJvcAKoBCUBfSzHBBkEEwEhAAQnCHNvbWVwcm9wAqgEKQF9LMcCAAMAASEABCsIc29tZXByb3ACqAQtAX0syAEBAQIBfZMHyAQvAQIBfZMHwQEGAQcBAAPBBDEBBwEABMcBGQEVAAgABDoEfQF9An0DfQTHAAgADgAIAAQ/BH0BfQJ9A30ExwQYBBkACAAERAR9AX0CfQN9BMcEIwQPASEABEkIc29tZXByb3ACqARLAX0swQAKAAkBxwEZBDoACAAETgR9AX0CfQN9BMcEEAQXAAgABFMEfQF9An0DfQTHAxsDHAAIAARYBH0BfQJ9A30ExwECAQ0BIQAEXQhzb21lcHJvcAKoBF8BfSzHAQQBBQAIAARhBH0BfQJ9A30ExwABAAYBIQAEZghzb21lcHJvcAKoBGgBfSzHAywDLQEhAARqCHNvbWVwcm9wAqgEbAF9LMcCCgMPASEABG4Ic29tZXByb3ACqARwAX0sxwMfAQABIQAEcghzb21lcHJvcAKoBHQBfSzHABcAGAEhAAR2CHNvbWVwcm9wAqgEeAF9LMcCEwMfAAgABHoEfQF9An0DfQTHARYBFwAIAAR/BH0BfQJ9A30ExwAIBD8BIQAEhAEIc29tZXByb3ACqASGAQF9LMcAGQAPAAgABIgBBH0BfQJ9A30ExwMBAScACAAEjQEEfQF9An0DfQTHAB4CDgEhAASSAQhzb21lcHJvcAKoBJQBAX0syAErAR4EfYQIfYQIfYQIfYQIxwB7AHwBIQAEmgEIc29tZXByb3ACqAScAQF9LMgBRgIrA32ICH2ICH2ICMgAEgAIAn2KCH2KCHADAAEBBWFycmF5AYcDAAEhAAMBCHNvbWVwcm9wAqgDAwF9LIEDAQEABIEDBQEABEECAAHIAw8CAAF9hwfIAxACAAF9hwfBAxECAAHHAAEAAgEhAAMTCHNvbWVwcm9wAqgDFQF9LIEEAAKIAxgBfYwHyAMPAxABfY8HwQMaAxAByAMbAxABfY8HyAMPAxoBfZAHyAMdAxoBfZAHxwACAw8BIQADHwhzb21lcHJvcAKoAyEBfSzHAxoDGwEhAAMjCHNvbWVwcm9wAqgDJQF9LMcCAAMAASEAAycIc29tZXByb3ACqAMpAX0swQMQAxEByAMrAxEBfZIHyAMsAxEBfZIHyAMtAxEBfZIHwQMYAxkBAATIAQYBBwF9lAfIAzQBBwF9lAfHAQcELwAIAAM2BH0BfQJ9A30EyAEBAR4CfZUHfZUHyAMsAy0DfZcHfZcHfZcHxwQTBBQBIQADQAhzb21lcHJvcAKoA0IBfSxIAAACfZgHfZgHyANFAAABfZgHxwEEAQUACAADRwR9AX0CfQN9BMgDQAQUAX2ZB8EDTAQUAscABgIXASEAA08Ic29tZXByb3ACqANRAX0syAM/Ay0BfZwHyAMfAQABfZ0HxwM2BC8ACAADVQR9AX0CfQN9BMcDRQNGASEAA1oIc29tZXByb3ACqANcAX0sxwMPAx0BIQADXghzb21lcHJvcAKoA2ABfSzIAQgBBgF9pAfIAQQDRwN9pwd9pwd9pwfIAA8AEAJ9rAd9rAfHAAAAAwAIAANoBH0BfQJ9A30EyAMQAysDfbIHfbIHfbIHxwQxAQcACAADcAR9AX0CfQN9BMcBAAQfASEAA3UIc29tZXByb3ACqAN3AX0syAM/A1MBfbUHyAN5A1MCfbUHfbUHyAMtAy4DfbcHfbcHfbcHyAACAhMCfbkHfbkHyAOAAQITAX25B8cBKwM7AAgAA4IBBH0BfQJ9A30ExwEZARUBIQADhwEIc29tZXByb3ACqAOJAQF9LMcCHAQLAAgAA4sBBH0BfQJ9A30EyAQZBCcBfbsHyAOQAQQnAn27B327B8cDkAEDkQEBIQADkwEIc29tZXByb3ACqAOVAQF9LMcDaAADAAgAA5cBBH0BfQJ9A30ExwN5A3oACAADnAEEfQF9An0DfQTHA4sBBAsACAADoQEEfQF9An0DfQTHA5MBA5EBASEAA6YBCHNvbWVwcm9wAqgDqAEBfSzHAAADaAAIAAOqAQR9AX0CfQN9BMgADgAZA328B328B328B8gECwQjBH2CCH2CCH2CCH2CCMcDLQN8ASEAA7YBCHNvbWVwcm9wAqgDuAEBfSzHBAoEAAAIAAO6AQR9AX0CfQN9BMgDgAEDgQECfYUIfYUIWgIAAQEFYXJyYXkBAARHAgAACAACBQR9AX0CfQN9BMECBQIAAQADwQIFAgoBAATBAAICBQEAA8cABgAHAAgAAhcEfQF9An0DfQTHAxkECwEhAAIcCHNvbWVwcm9wAqgCHgF9LMcABAAFASEAAiAIc29tZXByb3ACqAIiAX0syAAIAA4BfZYHyAMRAxIBfZoHxwMdAx4ACAACJgR9AX0CfQN9BMcEFgQRAAgAAisEfQF9An0DfQTHBAoEAAAIAAIwBH0BfQJ9A30EyAAOABkDfaAHfaAHfaAHxwEFACIACAACOAR9AX0CfQN9BMcDJwQrAAgAAj0EfQF9An0DfQTHAhcABwAIAAJCBH0BfQJ9A30EyAEABB8CfaUHfaUHxwQrAwABIQACSQhzb21lcHJvcAKoAksBfSzHBCcEEwAIAAJNBH0BfQJ9A30ExwMbAxwACAACUgR9AX0CfQN9BMcEJwJNASEAAlcIc29tZXByb3ACqAJZAX0sxwQvBDAACAACWwR9AX0CfQN9BMcCPQQrASEAAmAIc29tZXByb3ACqAJiAX0sxwAYAycBIQACZAhzb21lcHJvcAKoAmYBfSzIAQEBHgJ9swd9swfIAmQDJwN9tAd9tAd9tAfHAkkDAAAIAAJtBH0BfQJ9A30ExwJkAmoACAACcgR9AX0CfQN9BMcCJgMeAAgAAncEfQF9An0DfQTHAiUDEgEhAAJ8CHNvbWVwcm9wAqgCfgF9LMgBFwEYBH24B324B324B324B8cBAQJoASEAAoQBCHNvbWVwcm9wAqgChgEBfSzHAkkCbQAIAAKIAQR9AX0CfQN9BMcCSAQfASEAAo0BCHNvbWVwcm9wAqgCjwEBfSzIAQYEMQR9vgd9vgd9vgd9vgfHAAAAAwEhAAKVAQhzb21lcHJvcAKoApcBAX0sxwJNBBMBIQACmQEIc29tZXByb3ACqAKbAQF9LMcCJgJ3ASEAAp0BCHNvbWVwcm9wAqgCnwEBfSzHAAEABgAIAAKhAQR9AX0CfQN9BMgCjQEEHwF9gwjIAyMDGwF9hgjHBF0BDQAIAAKoAQR9AX0CfQN9BMcDPAEeAAgAAq0BBH0BfQJ9A30EagEAAQEFYXJyYXkByAEAAwABfYMHyAEBAwABfYMHwQECAwAByAEBAQIBfYYHyAEEAQIBfYYHyAEFAQIBfYYHwQEGAQIBxwEFAQYACAABCAR9AX0CfQN9BMEBAgEDAQAEwQEFAQgByAESAQgBfYsHyAETAQgBfYsHyAEUAQgBfYsHgQQAAYEBFgGIARcBfZEHxwEUARUACAABGQR9AX0CfQN9BMcBAQEEAAgAAR4EfQF9An0DfQTHARQBGQEhAAEjCHNvbWVwcm9wAqgBJQF9LMEDAQMFAQADxwEBAR4BIQABKwhzb21lcHJvcAKoAS0BfSzHAgUAHgEhAAEvCHNvbWVwcm9wAqgBMQF9LMcECwQjASEAATMIc29tZXByb3ACqAE1AX0sxwMtAy4ACAABNwR9AX0CfQN9BMcDDwMdAAgAATwEfQF9An0DfQTHAQIBDQAIAAFBBH0BfQJ9A30ExwQWBBEBIQABRghzb21lcHJvcAKoAUgBfSzBABgDJwHIAUoDJwF9nwfHBBcEGgAIAAFMBH0BfQJ9A30ExwEABB8BIQABUQhzb21lcHJvcAKoAVMBfSzIAx0DHgJ9oQd9oQfIARkBFQF9ogfIAhwECwN9qAd9qAd9qAfIAxEDEgF9qgfIBAABFgJ9qwd9qwfIABAAEQF9rQfIAV4AEQF9rQfIAV8AEQJ9rQd9rQfIAV4BXwR9rwd9rwd9rwd9rwfIABABXgN9sAd9sAd9sAfIAWgBXgF9sAfHBA8EEAAIAAFqBH0BfQJ9A30ExwQYBBkBIQABbwhzb21lcHJvcAKoAXEBfSzHAAcAEgEhAAFzCHNvbWVwcm9wAqgBdQF9LEcAAAAIAAF3BH0BfQJ9A30ExwMPATwBIQABfAhzb21lcHJvcAKoAX4BfSzIAXwBPAJ9ugd9ugfBAYEBATwCxwFoAWkACAABhAEEfQF9An0DfQTHAV8BYAAIAAGJAQR9AX0CfQN9BMcADgAZASEAAY4BCHNvbWVwcm9wAqgBkAEBfSzIAx8BAAF9vQfIAZIBAQABfb0HyAQVBBYCfb8Hfb8HxwQaBBgBIQABlgEIc29tZXByb3ACqAGYAQF9LMgBHgEEA32ACH2ACH2ACMcEGAFvAAgAAZ0BBH0BfQJ9A30ExwMTAAIBIQABogEIc29tZXByb3ACqAGkAQF9LMcBkgEBkwEBIQABpgEIc29tZXByb3ACqAGoAQF9LMcBnAEBBAEhAAGqAQhzb21lcHJvcAKoAawBAX0syAF8AYABBH2HCH2HCH2HCH2HCMgBpgEBkwEDfYkIfYkIfYkIYQAAAQEFYXJyYXkBiAAAAX2AB4EAAQHBAAAAAQLIAAQAAQF9gQfIAAEAAgF9hAfIAAYAAgF9hAfIAAcAAgF9hAfBAAgAAgHBAAgACQEAA8gACAAKAX2FB8EADgAKAcgADwAKAX2FB8gAEAAKAX2FB8cABwAIAAgAABIEfQF9An0DfQTIAgADAAF9iQfIABcDAAF9iQfHAA4ADwAIAAAZBH0BfQJ9A30ExwIFAgABIQAAHghzb21lcHJvcAKoACABfSzHAQUBEgEhAAAiCHNvbWVwcm9wAqgAJAF9LMcAHgIOAAgAACYEfQF9An0DfQTHBBQEFQAIAAArBH0BfQJ9A30ExwAAAAMACAAAMAR9AX0CfQN9BMcBBQAiAAgAADUEfQF9An0DfQTIAx4DGgN9mwd9mwd9mwfHAhcABwAIAAA9BH0BfQJ9A30ExwEYAxcBIQAAQghzb21lcHJvcAKoAEQBfSzBACIBEgEABMcDDwMdASEAAEsIc29tZXByb3ACqABNAX0sxwQYBBkBIQAATwhzb21lcHJvcAKoAFEBfSzHACIARgAIAABTBH0BfQJ9A30ExwMdAx4BIQAAWAhzb21lcHJvcAKoAFoBfSzIAB4AJgF9owfHAzYELwAIAABdBH0BfQJ9A30EyAQwAQIDfaYHfaYHfaYHyABkAQIBfakHyAAXABgCfa4Hfa4HxwQjBA8BIQAAaAhzb21lcHJvcAKoAGoBfSzHAycEKwAIAABsBH0BfQJ9A30ExwABAAYACAAAcQR9AX0CfQN9BMcAZABlAAgAAHYEfQF9An0DfQTIAAcAEgF9sQfIAHsAEgN9sQd9sQd9sQfIAA8AEAF9tgfHARMBFAAIAACAAQR9AX0CfQN9BMcDIwMbAAgAAIUBBH0BfQJ9A30ExwEVAQgACAAAigEEfQF9An0DfQTHAIoBAQgBIQAAjwEIc29tZXByb3ACqACRAQF9LMcCFwA9AAgAAJMBBH0BfQJ9A30ExwEYAEIACAAAmAEEfQF9An0DfQTHAzQDNQEhAACdAQhzb21lcHJvcAKoAJ8BAX0sxwAQABEBIQAAoQEIc29tZXByb3ACqACjAQF9LMgAgAEBFAF9gQjHBBYEEQEhAACmAQhzb21lcHJvcAKoAKgBAX0sxwAHAHsACAAAqgEEfQF9An0DfQQFABAAAQIDCQUPAR8CIwJDAkYFTAJQAlkCaQKQAQKeAQKiAQKnAQICDgAFCg0dAiECSgJYAmECZQJ9AoUBAo4BApYBApoBAp4BAgQUBAcMAhACGQEfBCQCKAIsAjEJSgJNAV4CZwJrAm8CcwJ3AoUBApMBApsBAgMWAAECAgULEgEUAhcCGwEgAiQCKAIrAS8FQQJNAlACWwJfAnYCiAEClAECpwECtwECARYAAQMBBwENBhYCJAInBCwCMAI0AkcCSgFSAnACdAJ9AoIBAo8BApcBAqMBAqcBAqsBAg==' const oldVal = JSON.parse('[[1,2,3,4],472,472,{"someprop":44},472,[1,2,3,4],{"someprop":44},[1,2,3,4],[1,2,3,4],[1,2,3,4],{"someprop":44},449,448,[1,2,3,4],[1,2,3,4],{"someprop":44},452,{"someprop":44},[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],452,[1,2,3,4],497,{"someprop":44},497,497,497,{"someprop":44},[1,2,3,4],522,522,452,470,{"someprop":44},[1,2,3,4],453,{"someprop":44},480,480,480,508,508,508,[1,2,3,4],[1,2,3,4],502,492,492,453,{"someprop":44},496,496,496,[1,2,3,4],496,493,495,495,495,495,493,[1,2,3,4],493,493,453,{"someprop":44},{"someprop":44},505,505,517,517,505,[1,2,3,4],{"someprop":44},509,{"someprop":44},521,521,521,509,477,{"someprop":44},{"someprop":44},485,485,{"someprop":44},515,{"someprop":44},451,{"someprop":44},[1,2,3,4],516,516,516,516,{"someprop":44},499,499,469,469,[1,2,3,4],[1,2,3,4],512,512,512,{"someprop":44},454,487,487,487,[1,2,3,4],[1,2,3,4],454,[1,2,3,4],[1,2,3,4],{"someprop":44},[1,2,3,4],459,[1,2,3,4],513,459,{"someprop":44},[1,2,3,4],482,{"someprop":44},[1,2,3,4],[1,2,3,4],459,[1,2,3,4],{"someprop":44},[1,2,3,4],484,454,510,510,510,510,468,{"someprop":44},468,[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],467,[1,2,3,4],467,486,486,486,[1,2,3,4],489,451,[1,2,3,4],{"someprop":44},[1,2,3,4],[1,2,3,4],{"someprop":44},{"someprop":44},483,[1,2,3,4],{"someprop":44},{"someprop":44},{"someprop":44},{"someprop":44},519,519,519,519,506,506,[1,2,3,4],{"someprop":44},464,{"someprop":44},481,481,[1,2,3,4],{"someprop":44},[1,2,3,4],464,475,475,475,463,{"someprop":44},[1,2,3,4],518,[1,2,3,4],[1,2,3,4],463,455,498,498,498,466,471,471,471,501,[1,2,3,4],501,501,476,{"someprop":44},466,[1,2,3,4],{"someprop":44},503,503,503,466,455,490,474,{"someprop":44},457,494,494,{"someprop":44},457,479,{"someprop":44},[1,2,3,4],500,500,500,{"someprop":44},[1,2,3,4],[1,2,3,4],{"someprop":44},{"someprop":44},{"someprop":44},[1,2,3,4],[1,2,3,4],{"someprop":44},[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3],491,491,[1,2,3,4],504,504,504,504,465,[1,2,3,4],{"someprop":44},460,{"someprop":44},488,488,488,[1,2,3,4],[1,2,3,4],{"someprop":44},{"someprop":44},514,514,514,514,{"someprop":44},{"someprop":44},{"someprop":44},458,[1,2,3,4],[1,2,3,4],462,[1,2,3,4],[1,2,3,4],{"someprop":44},462,{"someprop":44},[1,2,3,4],{"someprop":44},[1,2,3,4],507,{"someprop":44},{"someprop":44},507,507,{"someprop":44},{"someprop":44},[1,2,3,4],{"someprop":44},461,{"someprop":44},473,461,[1,2,3,4],461,511,511,461,{"someprop":44},{"someprop":44},520,520,520,[1,2,3,4],458]') const doc = new Y.Doc() @@ -21,9 +21,9 @@ export const testArrayCompatibilityV1 = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testMapDecodingCompatibilityV1 = tc => { +export const testMapDecodingCompatibilityV1 = _tc => { const oldDoc = 'BVcEAKEBAAGhAwEBAAShBAABAAGhBAECoQEKAgAEoQQLAQAEoQMcAaEEFQGhAiECAAShAS4BoQQYAaEEHgGhBB8BoQQdAQABoQQhAaEEIAGhBCMBAAGhBCUCoQQkAqEEKAEABKEEKgGhBCsBoQQwAQABoQQxAaEEMgGhBDQBAAGhBDYBoQQ1AQAEoQQ5AQABoQQ4AQAEoQM6AQAEoQRFAaEESgEAAaEESwEABKEETQGhBEABoQRSAgABoQRTAgAEoQRVAgABoQReAaEEWAEABKEEYAEAAaEEZgKhBGECAAShBGsBAAGhAaUBAgAEoQRwAgABoQRzAQAEoQR5AQABoQSAAQEABKEEggEBAAGhBIcBAwABoQGzAQEAAaEEjQECpwHMAQAIAASRAQR9AX0CfQN9BGcDACEBA21hcAN0d28BoQMAAQAEoQMBAQABoQMGAQAEIQEDbWFwA29uZQOhBAEBAAShAw8CoQMQAaEDFgEAAaEDFwEAAaEDGAGhAxwBAAGhAx0BoQIaBAAEoQMjAgABoQMpAQABoQMfAQABoQMrAQABoQMvAaEDLQEAAaEDMQIABKEDNQGhAzIBoQM6AaEDPAGhBCMBAAGhAU8BAAGhA0ADoQJCAQABoQNEAgABoQNFAgAEoQJEAQABoQNLAaEEQAEABKEESgEAAaEDWAGhA1MBAAGhA1oBAAGhA10DoQNbAQABoQNhAwABoQNiAQAEoQNmAaEDaAWhA20BAAShA3IBAAGhA3MBAAShA3gBoQN6A6EDfwEAAaEDgwEBAAShA4UBAgABoQOLAQGhA4IBAaEDjQEBoQOOAQEAAaEDjwEBAAShA5ABAaEDkgEBoQOXAQEABKEDmQEBAAGhA5gBAQABoQOgAQEAAaEDngEBaQIAIQEDbWFwA3R3bwGhAwABoQEAAQABoQIBAQAEoQIEAaEDAQKhAQwDAAShAg4BAAShAhMBoQQJBAABoQQVAQABoQIeAaECHAGhBBgBAAShAiICAAShBB4BAAShBB8BAAGhAzwBAAGhBCMCoQM9AqEDPgEAAaECOQEABKECPAGhAkEBAAGhAjoBAAGhAkIBAAShAkQBAAShAksBAAGhA0UCAAShAlMCoQJQAQAEoQJZAaECWgEAAaECYAKhAl8BAAShAmMCAAGhAmoBoQJkAgAEoQRSAQABoQJzAQAEoQRTAQAEoQJ6AQAEoQJ1AQABoQKEAQEABKEChgEBoQJ/AgABoQKLAQEABKECjwECAAGhApUBAQABoQKXAQGhAo0BAQABoQKaAQGhApkBAQABoQKcAQEAAaECnwEBAAShAqEBAaECnQEBAAShAqYBAaECpwEBAAGhAqwBAQABoQKtAQEABKECrwECAAF5AQAhAQNtYXADb25lASEBA21hcAN0d28CAAGhAQABoQMAAaEBBAEAAaEBBQGhAQYBoQMPAaEEAQGhAQoBoQELAaEBDAEABKEBDgEAAaEBDQEABKEBFQEABKEDHAKhBBUBAAShASECAAShAScBoQQWAwAEoQIhAgABoQEvAaECIgEABKEBOAEAAaEBNwEAAaEBPwGhAzoBAAShAUIBoQQjAQABoQFIAQABoQFKAaEDPgEAAaECOgEAAaEBTwIAAaEBUgEAAaECQQGhAVQCoQFWAgABoQFYAQAEoQFcAqEBWgEAAaEBYgShAWMBAAGhAWgBAAGhAWkCAAGhAW4BAAGhAWsBAAShAXABAAShAXcCAAShAX0BAAShAYIBAaEBcgEAAaEBhwEBoQGIAQEABKEBigEBAAShAZABAQAEoQGLAQIABKEBlQEBAAShBGkEAAGhAagBAQAEoQRzAQABoQGvAQKhBHsBAAGhAbMBAgAEoQSAAQEAAaEBuwECAAGhAbYBAqEEiwEBAAShAcIBAQAEoQHHAQEABKEEkAEBpwHRAQEoAAHSAQdkZWVwa2V5AXcJZGVlcHZhbHVloQHMAQFiAAAhAQNtYXADb25lAwABoQACASEBA21hcAN0d28CAAShAAQBoQAGAaEACwEAAaEADQIABKEADAEABKEAEAEABKEAGgEABKEAHwEABKEAFQGhACQBAAGhACoBoQApAaEALAGhAC0BoQAuAaEALwEAAaEAMAIAAaEANAEABKEAMQEABKEANgEAAaEAQAIAAaEAOwGhAEMCAAShAEcBAAShAEwBoQBFAQAEoQBRAQAEoQBXAqEAUgEABKEAXgIAAaEAZAKhAF0BoQBnAqEAaAEABKEAawGhAGoCoQBwAQABoQBzAQAEoQB1AQABoQB6AaEAcgGhAHwBAAShAH4BoQB9AgABoQCFAQEABKEAhwEBAAShAIwBAaEAgwEBAAShAJIBAQAEoQCXAQIABKEAkQEBAAGhAJ0BAQAEoQCiAQEABKEApAECAAGhAK8BAqEAqQEBAAGhALMBAQABBQABALcBAQIA0gHUAQEEAQCRAQMBAKUBAgEAuQE=' // eslint-disable-next-line const oldVal = /** @type {any} */ ({"one":[1,2,3,4],"two":{"deepkey":"deepvalue"}}) @@ -33,13 +33,13 @@ export const testMapDecodingCompatibilityV1 = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testTextDecodingCompatibilityV1 = tc => { +export const testTextDecodingCompatibilityV1 = _tc => { const oldDoc = 'BS8EAAUBBHRleHRveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9RAQAATHBBAEEAAHBBAIEAAHEBAMEAAQxdXUKxQQCBANveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAlveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xgMBAwIGaXRhbGljBHRydWXGBAsDAgVjb2xvcgYiIzg4OCLEBAwDAgExxAQNAwIBMsEEDgMCAsYEEAMCBml0YWxpYwRudWxsxgQRAwIFY29sb3IEbnVsbMQDAQQLATHEBBMECwIyOcQEFQQLCzl6anpueXdvaHB4xAQgBAsIY25icmNhcQrBAxADEQHGAR8BIARib2xkBHRydWXGAgACAQRib2xkBG51bGzFAwkECm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GARABEQZpdGFsaWMEdHJ1ZcYELQERBWNvbG9yBiIjODg4IsYBEgETBml0YWxpYwRudWxsxgQvARMFY29sb3IEbnVsbMYCKwIsBGJvbGQEdHJ1ZcYCLQIuBGJvbGQEbnVsbMYCjAECjQEGaXRhbGljBHRydWXGAo4BAo8BBml0YWxpYwRudWxswQA2ADcBxgQ1ADcFY29sb3IGIiM4ODgixgNlA2YFY29sb3IEbnVsbMYDUwNUBGJvbGQEdHJ1ZcQEOANUFjEzMTZ6bHBrbWN0b3FvbWdmdGhicGfGBE4DVARib2xkBG51bGzGAk0CTgZpdGFsaWMEdHJ1ZcYEUAJOBWNvbG9yBiIjODg4IsYCTwJQBml0YWxpYwRudWxsxgRSAlAFY29sb3IEbnVsbMYChAEChQEGaXRhbGljBHRydWXGBFQChQEFY29sb3IGIiM4ODgixgKGAQKHAQZpdGFsaWMEbnVsbMYEVgKHAQVjb2xvcgRudWxsxAMpAyoRMTMyMWFwZ2l2eWRxc2pmc2XFBBIDAm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn0zAwAEAQR0ZXh0AjEyhAMBAzkwboQDBAF4gQMFAoQDBwJyCsQDBAMFBjEyOTd6bcQDDwMFAXbEAxADBQFwwQMRAwUBxAMSAwUFa3pxY2rEAxcDBQJzYcQDGQMFBHNqeQrBAxIDEwHBAAwAEAHEAA0ADgkxMzAyeGNpd2HEAygADgF5xAMpAA4KaGhlenVraXF0dMQDMwAOBWhudGsKxgMoAykEYm9sZAR0cnVlxAM5AykGMTMwNXJswQM/AykCxANBAykDZXlrxgNEAykEYm9sZARudWxsxAMzAzQJMTMwN3R2amllwQNOAzQCxANQAzQDamxoxANTAzQCZ3bEA1UDNAJsYsQDVwM0AmYKxgNBA0IEYm9sZARudWxswQNaA0ICxANcA0ICMDjBA14DQgLEA2ADQgEKxgNhA0IEYm9sZAR0cnVlxQIaAhtveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9wQA3ADgCwQNlADgBxANmADgKMTVteml3YWJ6a8EDcAA4AsQDcgA4BnJybXNjdsEDeAA4AcQCYgJjATHEA3oCYwIzMsQDfAJjCTRyb3J5d3RoccQDhQECYwEKxAOFAQOGARkxMzI1aW9kYnppenhobWxpYnZweXJ4bXEKwQN6A3sBxgOgAQN7BWNvbG9yBiIjODg4IsYDfAN9Bml0YWxpYwRudWxsxgOiAQN9BWNvbG9yBG51bGxSAgAEAQR0ZXh0ATGEAgACMjiEAgIBOYECAwKEAgUBdYQCBgJ0Y4QCCAJqZYECCgKEAgwBaoECDQGBAg4BhAIPAnVmhAIRAQrEAg4CDwgxMjkycXJtZsQCGgIPAmsKxgIGAgcGaXRhbGljBHRydWXGAggCCQZpdGFsaWMEbnVsbMYCEQISBml0YWxpYwR0cnVlxAIfAhIBMcECIAISAsQCIgISAzRoc8QCJQISAXrGAiYCEgZpdGFsaWMEbnVsbMEAFQAWAsQCKQAWATDEAioAFgEwxAIrABYCaHjEAi0AFglvamVldHJqaHjBAjYAFgLEAjgAFgJrcsQCOgAWAXHBAjsAFgHBAjwAFgHEAj0AFgFuxAI+ABYCZQrGAiUCJgZpdGFsaWMEbnVsbMQCQQImAjEzwQJDAiYCxAJFAiYIZGNjeGR5eGfEAk0CJgJ6Y8QCTwImA2Fwb8QCUgImAnRuxAJUAiYBcsQCVQImAmduwQJXAiYCxAJZAiYBCsYCWgImBml0YWxpYwR0cnVlxAI6AjsEMTMwM8QCXwI7A3VodsQCYgI7BmdhbmxuCsUCVQJWb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcECPAI9AcECPgI/AcYDFwMYBml0YWxpYwR0cnVlxgJsAxgFY29sb3IGIiM4ODgixgMZAxoGaXRhbGljBG51bGzGAm4DGgVjb2xvcgRudWxswQMQBCkBxAJwBCkKMTMwOXpsZ3ZqeMQCegQpAWfBAnsEKQLGBA0EDgZpdGFsaWMEbnVsbMYCfgQOBWNvbG9yBG51bGzEAn8EDgUxMzEwZ8QChAEEDgJ3c8QChgEEDgZoeHd5Y2jEAowBBA4Ca3HEAo4BBA4Ec2RydcQCkgEEDgRqcWljwQKWAQQOBMQCmgEEDgEKxgKbAQQOBml0YWxpYwR0cnVlxgKcAQQOBWNvbG9yBiIjODg4IsECaAI7AcQCCgEBFjEzMThqd3NramFiZG5kcmRsbWphZQrGA1UDVgRib2xkBHRydWXGA1cDWARib2xkBG51bGzGAEAAQQZpdGFsaWMEdHJ1ZcYCtwEAQQRib2xkBG51bGzEArgBAEESMTMyNnJwY3pucWFob3BjcnRkxgLKAQBBBml0YWxpYwRudWxsxgLLAQBBBGJvbGQEdHJ1ZRkBAMUCAgIDb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcQCCgILBzEyOTN0agrGABgAGQRib2xkBHRydWXGAA0ADgRib2xkBG51bGxEAgAHMTMwNnJ1cMQBEAIAAnVqxAESAgANaWtrY2pucmNwc2Nrd8QBHwIAAQrFBBMEFG97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3FAx0DBW97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAlICUwRib2xkBHRydWXGAlQCVQRib2xkBG51bGzGAnsCfAZpdGFsaWMEdHJ1ZcYBJQJ8BWNvbG9yBiIjODg4IsYBJgJ8BGJvbGQEbnVsbMQBJwJ8CjEzMTRweWNhdnXGATECfAZpdGFsaWMEbnVsbMYBMgJ8BWNvbG9yBG51bGzBATMCfAHFADEAMm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GADUANgZpdGFsaWMEdHJ1ZcEANwA4AcQAMgAzEzEzMjJybmJhb2tvcml4ZW52cArEAgUCBhcxMzIzbnVjdnhzcWx6bndsZmF2bXBjCsYDDwMQBGJvbGQEdHJ1ZR0AAMQEAwQEDTEyOTVxZnJ2bHlmYXDEAAwEBAFjxAANBAQCanbBAAwADQHEABAADQEywQARAA0ExAAVAA0DZHZmxAAYAA0BYcYCAwIEBml0YWxpYwR0cnVlwQAaAgQCxAAcAgQEMDRrdcYAIAIEBml0YWxpYwRudWxsxQQgBCFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQJAABZveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xAQVBBYGMTMxMWtrxAIqAisIMTMxMnFyd3TEADECKwFixAAyAisDcnhxxAA1AisBasQANgIrAXjEADcCKwZkb3ZhbwrEAgAEKwMxMzHEAEAEKwkzYXhoa3RoaHXGAnoCewRib2xkBG51bGzFAEoCe297ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAEsCewRib2xkBHRydWXEAl8CYBExMzE3cGZjeWhrc3JrcGt0CsQBHwQqCzEzMTliY2Nna3AKxAKSAQKTARUxMzIwY29oYnZjcmtycGpuZ2RvYwoFBAQCAg8CKQE1AQADEAESBBsCAwsGAhIBHgJAAk8CWwJfAmQDcQJ5AaABAQIOBAILAg4CIQIoAjcCPAJEAlgCagJwAXwClwEEngEBAQI0ATcB' // eslint-disable-next-line const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"291311kk9zjznywohpx"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"xr\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}}] const doc = new Y.Doc() Y.applyUpdate(doc, buffer.fromBase64(oldDoc)) - t.compare(doc.getText('text').toDelta(), oldVal) + t.compare(doc.getText('text').getContent().toJSON(), oldVal) } diff --git a/tests/delta.tests.js b/tests/delta.tests.js index 0237c974b..d7de0eb77 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -6,7 +6,7 @@ import * as delta from '../src/utils/Delta.js' */ export const testDelta = _tc => { const d = delta.createTextDelta().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ insert: ['tester'] }).insert('!').done() - t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }]) + t.compare(d.toJSON(), [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }]) } /** @@ -21,5 +21,59 @@ export const testDeltaMerging = _tc => { .insert([1]) .insert([2]) .done() - t.compare(d.toJSON().ops, [{ insert: 'helloworld' }, { insert: ' ', attributes: { italic: true } }, { insert: {} }, { insert: [1, 2] }]) + t.compare(d.toJSON(), [{ insert: 'helloworld' }, { insert: ' ', attributes: { italic: true } }, { insert: {} }, { insert: [1, 2] }]) +} + +/** + * @param {t.TestCase} _tc + */ +export const testUseAttributes = _tc => { + const d = delta.createTextDelta() + .insert('a') + .updateUsedAttributes('bold', true) + .insert('b') + .insert('c', { bold: 4 }) + .updateUsedAttributes('bold', null) + .insert('d') + .useAttributes({ italic: true }) + .insert('e') + .useAttributes(null) + .insert('f') + .done() + const d2 = delta.createTextDelta() + .insert('a') + .insert('b', { bold: true }) + .insert('c', { bold: 4 }) + .insert('d') + .insert('e', { italic: true }) + .insert('f') + .done() + t.compare(d, d2) +} + +/** + * @param {t.TestCase} _tc + */ +export const testUseAttribution = _tc => { + const d = delta.createTextDelta() + .insert('a') + .updateUsedAttribution('insert', ['me']) + .insert('b') + .insert('c', null, { insert: ['you'] }) + .updateUsedAttribution('insert', null) + .insert('d') + .useAttribution({ insert: ['me'] }) + .insert('e') + .useAttribution(null) + .insert('f') + .done() + const d2 = delta.createTextDelta() + .insert('a') + .insert('b', null, { insert: ['me'] }) + .insert('c', null, { insert: ['you'] }) + .insert('d') + .insert('e', null, { insert: ['me'] }) + .insert('f') + .done() + t.compare(d, d2) } diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 80db1b281..1387a43c8 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -67,31 +67,6 @@ export const testFindTypeInOtherDoc = _tc => { t.assert(findTypeInOtherYdoc(ytext, ydocClone) != null) } -/** - * @param {t.TestCase} _tc - */ -export const testOriginInTransaction = _tc => { - const doc = new Y.Doc() - const ytext = doc.getText() - /** - * @type {Array} - */ - const origins = [] - doc.on('afterTransaction', (tr) => { - origins.push(tr.origin) - if (origins.length <= 1) { - ytext.toDelta(Y.snapshot(doc)) // adding a snapshot forces toDelta to create a cleanup transaction - doc.transact(() => { - ytext.insert(0, 'a') - }, 'nested') - } - }) - doc.transact(() => { - ytext.insert(0, '0') - }, 'first') - t.compareArrays(origins, ['first', 'cleanup', 'nested']) -} - /** * Client id should be changed when an instance receives updates from another client using the same client id. * diff --git a/tests/testHelper.js b/tests/testHelper.js index d891df2e5..a84cd3c8d 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -465,7 +465,7 @@ export const compare = users => { const userArrayValues = users.map(u => u.getArray('array').toJSON()) const userMapValues = users.map(u => u.getMap('map').toJSON()) const userXmlValues = users.map(u => u.get('xml', Y.XmlElement).toString()) - const userTextValues = users.map(u => u.getText('text').toDelta()) + const userTextValues = users.map(u => u.getText('text').getContent()) for (const u of users) { t.assert(u.store.pendingDs === null) t.assert(u.store.pendingStructs === null) @@ -490,7 +490,7 @@ export const compare = users => { t.compare(userArrayValues[i], userArrayValues[i + 1]) t.compare(userMapValues[i], userMapValues[i + 1]) t.compare(userXmlValues[i], userXmlValues[i + 1]) - t.compare(userTextValues[i].map(/** @param {any} a */ a => typeof a.insert === 'string' ? a.insert : ' ').join('').length, users[i].getText('text').length) + t.compare(userTextValues[i].ops.map(/** @param {any} a */ a => typeof a.insert === 'string' ? a.insert : ' ').join('').length, users[i].getText('text').length) t.compare(userTextValues[i], userTextValues[i + 1], '', (_constructor, a, b) => { if (a instanceof Y.AbstractType) { t.compare(a.toJSON(), b.toJSON()) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 46aa855ae..0e833d193 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -1,6 +1,7 @@ import * as Y from '../src/index.js' import { init } from './testHelper.js' // eslint-disable-line import * as t from 'lib0/testing' +import * as delta from '../src/utils/Delta.js' export const testInconsistentFormat = () => { /** @@ -10,7 +11,7 @@ export const testInconsistentFormat = () => { const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText)) content.format(0, 6, { bold: null }) content.format(6, 4, { type: 'text' }) - t.compare(content.toDelta(), [ + t.compare(content.getContent(), delta.fromJSON([ { attributes: { type: 'text' }, insert: 'Merge Test' @@ -19,11 +20,10 @@ export const testInconsistentFormat = () => { attributes: { type: 'text', italic: true }, insert: ' After' } - ]) + ])) } const initializeYDoc = () => { const yDoc = new Y.Doc({ gc: false }) - const content = /** @type {Y.XmlText} */ (yDoc.get('text', Y.XmlText)) content.insert(0, ' After', { type: 'text', italic: true }) content.insert(0, 'Test', { type: 'text' }) @@ -94,11 +94,11 @@ export const testUndoText = tc => { t.assert(text0.toString() === 'bcxyz') // test marks text0.format(1, 3, { bold: true }) - t.compare(text0.toDelta(), [{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]) + t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) undoManager.undo() - t.compare(text0.toDelta(), [{ insert: 'bcxyz' }]) + t.compare(text0.getContent(), delta.fromJSON([{ insert: 'bcxyz' }])) undoManager.redo() - t.compare(text0.toDelta(), [{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]) + t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) } /** @@ -686,16 +686,16 @@ export const testUndoDeleteTextFormat = _tc => { undoManager.undo() Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc)) - const expect = [ + const expect = delta.fromJSON([ { insert: 'Attack ships ' }, { insert: 'on fire', attributes: { bold: true } }, { insert: ' off the shoulder of Orion.' } - ] - t.compare(text.toDelta(), expect) - t.compare(text2.toDelta(), expect) + ]) + t.compare(text.getContent(), expect) + t.compare(text2.getContent(), expect) } /** diff --git a/tests/updates.tests.js b/tests/updates.tests.js index d517fb928..08468365b 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -126,7 +126,7 @@ export const testKeyEncoding = tc => { const update = Y.encodeStateAsUpdateV2(users[0]) Y.applyUpdateV2(users[1], update) - t.compare(text1.toDelta(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) + t.compare(text1.getContent().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) compare(users) } @@ -331,7 +331,7 @@ export const testObfuscateUpdates = _tc => { const omap = odoc.getMap('map') const oarray = odoc.getArray('array') // test ytext - const delta = otext.toDelta() + const delta = /** @type {Array} */ (otext.getContent().toJSON()) t.assert(delta.length === 2) t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4) t.assert(object.length(delta[0].attributes) === 1) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 13bdd8946..b37cc4595 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -3,7 +3,7 @@ import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' import * as delta from '../src/utils/Delta.js' -import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager } from 'yjs/internals' +import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager, createAttributionManagerFromSnapshots } from 'yjs/internals' const { init, compare } = Y @@ -232,185 +232,138 @@ export const testDeltaBug = _tc => { } ] ytext.applyDelta(addingList) - const result = ytext.toDelta() - const expectedResult = [ - { - attributes: { - 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' - }, - insert: '\n' - }, - { - attributes: { - 'table-col': { - width: '150' - } - }, - insert: '\n\n\n' - }, - { - attributes: { - 'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-6kv2ls', - cell: 'cell-apba4k' - }, - row: 'row-6kv2ls', - cell: 'cell-apba4k', + const result = ytext.getContent() + const expectedResult = delta.createTextDelta() + .insert('\n', { 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' }) + .insert('\n\n\n', { 'table-col': { width: '150' } }) + .insert('\n', { + 'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea', + 'table-cell-line': { rowspan: '1', - colspan: '1' - }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-6kv2ls', - cell: 'cell-a8qf0r' - }, + colspan: '1', row: 'row-6kv2ls', - cell: 'cell-a8qf0r', - rowspan: '1', - colspan: '1' + cell: 'cell-apba4k' }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-6kv2ls', - cell: 'cell-oi9ikb' - }, - row: 'row-6kv2ls', - cell: 'cell-oi9ikb', + row: 'row-6kv2ls', + cell: 'cell-apba4k', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c', + 'table-cell-line': { rowspan: '1', - colspan: '1' + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-a8qf0r' }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-d1sv2g', - cell: 'cell-dt6ks2' - }, - row: 'row-d1sv2g', - cell: 'cell-dt6ks2', + row: 'row-6kv2ls', + cell: 'cell-a8qf0r', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6', + 'table-cell-line': { rowspan: '1', - colspan: '1' + colspan: '1', + row: 'row-6kv2ls', + cell: 'cell-oi9ikb' }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-d1sv2g', - cell: 'cell-qah2ay' - }, - row: 'row-d1sv2g', - cell: 'cell-qah2ay', + row: 'row-6kv2ls', + cell: 'cell-oi9ikb', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627', + 'table-cell-line': { rowspan: '1', - colspan: '1' - }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-468a69b5-9332-450b-9107-381d593de249', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-d1sv2g', - cell: 'cell-fpcz5a' - }, + colspan: '1', row: 'row-d1sv2g', - cell: 'cell-fpcz5a', + cell: 'cell-dt6ks2' + }, + row: 'row-d1sv2g', + cell: 'cell-dt6ks2', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f', + 'table-cell-line': { rowspan: '1', - colspan: '1' + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-qah2ay' }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-pflz90', - cell: 'cell-zrhylp' - }, - row: 'row-pflz90', - cell: 'cell-zrhylp', + row: 'row-d1sv2g', + cell: 'cell-qah2ay', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-468a69b5-9332-450b-9107-381d593de249', + 'table-cell-line': { rowspan: '1', - colspan: '1' + colspan: '1', + row: 'row-d1sv2g', + cell: 'cell-fpcz5a' }, - insert: '\n' - }, - { - attributes: { - 'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-pflz90', - cell: 'cell-s1q9nt' - }, + row: 'row-d1sv2g', + cell: 'cell-fpcz5a', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c', + 'table-cell-line': { + rowspan: '1', + colspan: '1', row: 'row-pflz90', - cell: 'cell-s1q9nt', + cell: 'cell-zrhylp' + }, + row: 'row-pflz90', + cell: 'cell-zrhylp', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b', + 'table-cell-line': { rowspan: '1', - colspan: '1' + colspan: '1', + row: 'row-pflz90', + cell: 'cell-s1q9nt' }, - insert: '\n' - }, - { - insert: '\n', - // This attributes has only list and no table-cell-line - attributes: { - list: { - rowspan: '1', - colspan: '1', - row: 'row-pflz90', - cell: 'cell-20b0j9', - list: 'bullet' - }, - 'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484', + row: 'row-pflz90', + cell: 'cell-s1q9nt', + rowspan: '1', + colspan: '1' + }) + // This attributes has only list and no table-cell-line + .insert('\n', { + list: { + rowspan: '1', + colspan: '1', row: 'row-pflz90', cell: 'cell-20b0j9', - rowspan: '1', - colspan: '1' - } - }, - // No table-cell-line below here - { - attributes: { - 'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3' - }, - insert: '\n' - }, - { - insert: 'Content after table' - }, - { - attributes: { - 'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce' + list: 'bullet' }, - insert: '\n' - } - ] + 'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484', + row: 'row-pflz90', + cell: 'cell-20b0j9', + rowspan: '1', + colspan: '1' + }) + // No table-cell-line below here + .insert('\n', { + 'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3' + }) + .insert('Content after table') + .insert('\n', { + 'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce' + }) + .done() t.compare(result, expectedResult) } @@ -477,11 +430,7 @@ export const testDeltaBug2 = _tc => { insert: '\n', attributes: { 'block-id': 'block-8a1d2bb6-23c2-4bcf-af3c-3919ffea1697' } }, - { insert: '\n\n', attributes: { 'table-col': { width: '150' } } }, - { - insert: '\n', - attributes: { 'table-col': { width: '150' } } - }, + { insert: '\n\n\n', attributes: { 'table-col': { width: '150' } } }, { insert: '\n', attributes: { @@ -1640,8 +1589,8 @@ export const testDeltaBug2 = _tc => { } ] ytext.applyDelta(changeEvent) - const delta = ytext.toDelta() - t.compare(delta[41], { + const delta = ytext.getContent() + t.compare(delta.ops[40].toJSON(), { insert: '\n', attributes: { 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' @@ -1667,8 +1616,8 @@ export const testDeltaAfterConcurrentFormatting = tc => { */ const deltas = [] text1.observe(event => { - if (event.delta.length > 0) { - deltas.push(event.delta) + if (event.delta.ops.length > 0) { + deltas.push(event.delta.toJSON()) } }) testConnector.flushAllMessages() @@ -1680,10 +1629,10 @@ export const testDeltaAfterConcurrentFormatting = tc => { */ export const testBasicInsertAndDelete = tc => { const { users, text0 } = init(tc, { users: 2 }) - let delta + let eventDelta text0.observe(event => { - delta = event.delta + eventDelta = event.delta }) text0.delete(0, 0) @@ -1691,21 +1640,21 @@ export const testBasicInsertAndDelete = tc => { text0.insert(0, 'abc') t.assert(text0.toString() === 'abc', 'Basic insert works') - t.compare(delta, [{ insert: 'abc' }]) + t.compare(eventDelta, delta.fromJSON([{ insert: 'abc' }])) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete works (position 0)') - t.compare(delta, [{ delete: 1 }]) + t.compare(eventDelta, delta.fromJSON([{ delete: 1 }])) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(delta, [{ retain: 1 }, { delete: 1 }]) + t.compare(eventDelta, delta.fromJSON([{ retain: 1 }, { delete: 1 }])) users[0].transact(() => { text0.insert(0, '1') text0.delete(0, 1) }) - t.compare(delta, []) + t.compare(eventDelta, delta.fromJSON([])) compare(users) } @@ -1715,36 +1664,36 @@ export const testBasicInsertAndDelete = tc => { */ export const testBasicFormat = tc => { const { users, text0 } = init(tc, { users: 2 }) - let delta + let eventDelta text0.observe(event => { - delta = event.delta + eventDelta = event.delta }) text0.insert(0, 'abc', { bold: true }) t.assert(text0.toString() === 'abc', 'Basic insert with attributes works') - t.compare(text0.toDelta(), [{ insert: 'abc', attributes: { bold: true } }]) - t.compare(delta, [{ insert: 'abc', attributes: { bold: true } }]) + t.compare(text0.getContent(), delta.createTextDelta().insert('abc', { bold: true }).done()) + t.compare(eventDelta, delta.createTextDelta().insert('abc', { bold: true })) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)') - t.compare(text0.toDelta(), [{ insert: 'bc', attributes: { bold: true } }]) - t.compare(delta, [{ delete: 1 }]) + t.compare(text0.getContent(), delta.createTextDelta().insert('bc', { bold: true })) + t.compare(eventDelta, delta.createTextDelta().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(text0.toDelta(), [{ insert: 'b', attributes: { bold: true } }]) - t.compare(delta, [{ retain: 1 }, { delete: 1 }]) + t.compare(text0.getContent(), delta.createTextDelta().insert('b', { bold: true })) + t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1)) text0.insert(0, 'z', { bold: true }) t.assert(text0.toString() === 'zb') - t.compare(text0.toDelta(), [{ insert: 'zb', attributes: { bold: true } }]) - t.compare(delta, [{ insert: 'z', attributes: { bold: true } }]) + t.compare(text0.getContent(), delta.createTextDelta().insert('zb', { bold: true })) + t.compare(eventDelta, delta.createTextDelta().insert('z', { bold: true })) // @ts-ignore t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker') text0.insert(0, 'y') t.assert(text0.toString() === 'yzb') - t.compare(text0.toDelta(), [{ insert: 'y' }, { insert: 'zb', attributes: { bold: true } }]) - t.compare(delta, [{ insert: 'y' }]) + t.compare(text0.getContent(), delta.createTextDelta().insert('y').insert('zb', { bold: true })) + t.compare(eventDelta, delta.createTextDelta().insert('y')) text0.format(0, 2, { bold: null }) t.assert(text0.toString() === 'yzb') - t.compare(text0.toDelta(), [{ insert: 'yz' }, { insert: 'b', attributes: { bold: true } }]) - t.compare(delta, [{ retain: 1 }, { retain: 1, attributes: { bold: null } }]) + t.compare(text0.getContent(), delta.createTextDelta().insert('yz').insert('b', { bold: true })) + t.compare(eventDelta, delta.createTextDelta().retain(1).retain(1, { bold: null })) compare(users) } @@ -1755,16 +1704,16 @@ export const testFalsyFormats = tc => { const { users, text0 } = init(tc, { users: 2 }) let delta text0.observe(event => { - delta = event.delta + delta = event.delta.toJSON() }) text0.insert(0, 'abcde', { falsy: false }) - t.compare(text0.toDelta(), [{ insert: 'abcde', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }]) t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }]) text0.format(1, 3, { falsy: true }) - t.compare(text0.toDelta(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }]) text0.format(2, 1, { falsy: false }) - t.compare(text0.toDelta(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }]) compare(users) } @@ -1783,7 +1732,7 @@ export const testMultilineFormat = _tc => { { retain: 1 }, // newline character { retain: 10, attributes: { bold: true } } ]) - t.compare(testText.toDelta(), [ + t.compare(testText.getContent().toJSON(), [ { insert: 'Test', attributes: { bold: true } }, { insert: '\n' }, { insert: 'Multi-line', attributes: { bold: true } }, @@ -1804,7 +1753,7 @@ export const testNotMergeEmptyLinesFormat = _tc => { { insert: '\nText' }, { insert: '\n', attributes: { title: true } } ]) - t.compare(testText.toDelta(), [ + t.compare(testText.getContent().toJSON(), [ { insert: 'Text' }, { insert: '\n', attributes: { title: true } }, { insert: '\nText' }, @@ -1828,7 +1777,7 @@ export const testPreserveAttributesThroughDelete = _tc => { { delete: 1 }, { retain: 1, attributes: { title: true } } ]) - t.compare(testText.toDelta(), [ + t.compare(testText.getContent().toJSON(), [ { insert: 'Text' }, { insert: '\n', attributes: { title: true } } ]) @@ -1842,7 +1791,7 @@ export const testGetDeltaWithEmbeds = tc => { text0.applyDelta([{ insert: { linebreak: 's' } }]) - t.compare(text0.toDelta(), [{ + t.compare(text0.getContent().toJSON(), [{ insert: { linebreak: 's' } }]) } @@ -1855,18 +1804,18 @@ export const testTypesAsEmbed = tc => { text0.applyDelta([{ insert: new Y.Map([['key', 'val']]) }]) - t.compare(text0.toDelta()[0].insert.toJSON(), { key: 'val' }) + t.compare(/** @type {delta.InsertOp} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' }) let firedEvent = false text1.observe(event => { const d = event.delta - t.assert(d.length === 1) - t.compare(d.map(x => /** @type {Y.AbstractType} */ (x.insert).toJSON()), [{ key: 'val' }]) + t.assert(d.ops.length === 1) + t.compare(d.ops.map(x => /** @type {any} */ (x).insert.toJSON()), [{ key: 'val' }]) firedEvent = true }) testConnector.flushAllMessages() - const delta = text1.toDelta() + const delta = text1.getContent().toJSON() t.assert(delta.length === 1) - t.compare(delta[0].insert.toJSON(), { key: 'val' }) + t.compare(/** @type {any} */ (delta[0]).insert.toJSON(), { key: 'val' }) t.assert(firedEvent, 'fired the event observer containing a Type-Embed') } @@ -1898,18 +1847,13 @@ export const testSnapshot = tc => { }, { delete: 1 }]) - const state1 = text0.toDelta(snapshot1) - t.compare(state1, [{ insert: 'abcd' }]) - const state2 = text0.toDelta(snapshot2) - t.compare(state2, [{ insert: 'axcd' }]) - const state2Diff = text0.toDelta(snapshot2, snapshot1) - // @ts-ignore Remove userid info - state2Diff.forEach(v => { - if (v.attributes && v.attributes.ychange) { - delete v.attributes.ychange.user - } - }) - t.compare(state2Diff, [{ insert: 'a' }, { insert: 'x', attributes: { ychange: { type: 'added' } } }, { insert: 'b', attributes: { ychange: { type: 'removed' } } }, { insert: 'cd' }]) + const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) + t.compare(state1.toJSON(), [{ insert: 'abcd' }]) + const state2 = text0.getContent(createAttributionManagerFromSnapshots(snapshot2)) + t.compare(state2.toJSON(), [{ insert: 'axcd' }]) + const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON() + const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }] + t.compare(state2Diff, expected) } /** @@ -1928,8 +1872,8 @@ export const testSnapshotDeleteAfter = tc => { }, { insert: 'e' }]) - const state1 = text0.toDelta(snapshot1) - t.compare(state1, [{ insert: 'abcd' }]) + const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot1)) + t.compare(state1, delta.fromJSON([{ insert: 'abcd' }])) } /** @@ -1948,7 +1892,7 @@ export const testToDeltaEmbedAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 }) - const delta0 = text0.toDelta() + const delta0 = text0.getContent().toJSON() t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' }, attributes: { width: 100 } }, { insert: 'b', attributes: { bold: true } }]) } @@ -1959,7 +1903,7 @@ export const testToDeltaEmbedNoAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }) - const delta0 = text0.toDelta() + const delta0 = text0.getContent().toJSON() t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' } }, { insert: 'b', attributes: { bold: true } }], 'toDelta does not set attributes key when no attributes are present') } @@ -2000,7 +1944,7 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { }) testConnector.flushAllMessages() /** - * @type {Array} + * @type {Array} */ const deltas = [] text0.observe(event => { @@ -2011,9 +1955,9 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { }) text1.format(0, 1, { LIST_STYLES: 'number' }) testConnector.flushAllMessages() - const filteredDeltas = deltas.filter(d => d.length > 0) + const filteredDeltas = deltas.filter(d => d.ops.length > 0) t.assert(filteredDeltas.length === 2) - t.compare(filteredDeltas[0], [ + t.compare(filteredDeltas[0].toJSON(), [ { retain: 1, attributes: { LIST_STYLES: 'number' } } ]) t.compare(filteredDeltas[0], filteredDeltas[1]) @@ -2267,9 +2211,9 @@ export const testFormattingBug = async _tc => { { insert: '\n', attributes: { url: 'http://docs.yjs.dev' } }, { insert: '\n', attributes: { url: 'http://example.com' } } ] - t.compare(text1.toDelta(), expectedResult) - t.compare(text1.toDelta(), text2.toDelta()) - console.log(text1.toDelta()) + t.compare(text1.getContent().toJSON(), expectedResult) + t.compare(text1.getContent().toJSON(), text2.getContent().toJSON()) + console.log(text1.getContent().toJSON()) } /** @@ -2297,8 +2241,8 @@ export const testDeleteFormatting = _tc => { { insert: 'on ', attributes: { bold: true } }, { insert: 'fire off the shoulder of Orion.' } ] - t.compare(text.toDelta(), expected) - t.compare(text2.toDelta(), expected) + t.compare(text.getContent().toJSON(), expected) + t.compare(text2.getContent().toJSON(), expected) } /** @@ -2590,13 +2534,13 @@ const checkResult = result => { t.info('length of text = ' + result.users[i - 1].getText('text').length) t.measureTime('original toDelta perf', () => { - result.users[i - 1].getText('text').toDelta().map(typeToObject) + result.users[i - 1].getText('text').getContent().toJSON().map(typeToObject) }) t.measureTime('getContent(attributionManager) performance)', () => { result.users[i - 1].getText('text').getContent() }) - const p1 = result.users[i - 1].getText('text').toDelta().map(typeToObject) - const p2 = result.users[i].getText('text').toDelta().map(typeToObject) + const p1 = result.users[i - 1].getText('text').getContent().toJSON().map(typeToObject) + const p2 = result.users[i].getText('text').getContent().toJSON().map(typeToObject) t.compare(p1, p2) } // Uncomment this to find formatting-cleanup issues @@ -2613,7 +2557,7 @@ const checkResult = result => { * @param {t.TestCase} tc */ export const testAttributionManagerDefaultPerformance = tc => { - const N = 10000 + const N = 100000 const MaxDeletionLength = 5 // 25% chance of deletion const MaxInsertionLength = 5 const ydoc = new Y.Doc() @@ -2634,12 +2578,7 @@ export const testAttributionManagerDefaultPerformance = tc => { const M = 100 t.measureTime(`original toString perf `, () => { for (let i = 0; i < M; i++) { - ytext.toDelta() - } - }) - t.measureTime(`original toDelta perf `, () => { - for (let i = 0; i < M; i++) { - ytext.toDelta() + ytext.toString() } }) t.measureTime(`getContent(attributionManager) performance `, () => { diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 74b563588..251a0c384 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -207,7 +207,7 @@ export const testFormattingBug = _tc => { { insert: 'C', attributes: { em: {}, strong: {} } } ] yxml.applyDelta(delta) - t.compare(yxml.toDelta(), delta) + t.compare(yxml.getContent().toJSON(), delta) } /** @@ -366,8 +366,8 @@ export const testElementAttributedContentViaDiffer = _tc => { delta.createTextDelta().insert('bigworld') ]) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) - console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: null } }) From cb191e744e39578e4b02655c2bc74ad3ef026f65 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 7 May 2025 19:35:31 +0200 Subject: [PATCH 307/362] [y.text] event returns delta - fix a bunch of bugs --- attributing-content.md | 10 ---------- src/types/YText.js | 30 ++++++++++++++++-------------- src/utils/AttributionManager.js | 7 +++++-- src/utils/Delta.js | 4 ---- tests/testHelper.js | 2 +- tests/y-text.tests.js | 2 +- 6 files changed, 23 insertions(+), 32 deletions(-) diff --git a/attributing-content.md b/attributing-content.md index 5ed180b8e..72783e287 100644 --- a/attributing-content.md +++ b/attributing-content.md @@ -116,16 +116,6 @@ deletions and insertions only, without Attributions). `AttributionManager` is an abstract class for mapping attributions. It is possible to highlight arbitrary content with this approach. -The next steps are to: - -- finish the implementation for Y.Map and Y.Xml* (which should be easy, compared -to Y.Map). -- Implement an AttributionManager-CRDT for the backend that sits there and -associates changes with users. -- use `getContent(attributionManager)` instead of `toDelta` in y-prosemirror. -Would like to make the attribution part of y-prosemirror, however Nick can also -use this approach to customly render the changes in ProseMirror. - The AttributionManager is encodes very efficiently. The ids are encoded using run-length encoding and the Attributes are de-duplicated and only encoded once. The above example encodes in 20 bytes. diff --git a/src/types/YText.js b/src/types/YText.js index 059e3284d..794912790 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -448,7 +448,7 @@ const cleanupContextlessFormattingGap = (transaction, item) => { * * This function won't be exported anymore as soon as there is confidence that the YText type works as intended. * - * @param {YText} type + * @param {YText} type * @return {number} How many formatting attributes have been cleaned up. */ export const cleanupYTextFormatting = type => { @@ -485,7 +485,7 @@ export const cleanupYTextFormatting = type => { */ export const cleanupYTextAfterTransaction = transaction => { /** - * @type {Set} + * @type {Set>} */ const needFullCleanup = new Set() // check if another formatting item was inserted @@ -500,10 +500,10 @@ export const cleanupYTextAfterTransaction = transaction => { // cleanup in a new transaction transact(doc, (t) => { iterateStructsByIdSet(transaction, transaction.deleteSet, item => { - if (item instanceof GC || !(/** @type {YText} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YText} */ (item.parent))) { + if (item instanceof GC || !(/** @type {YText} */ (item.parent)._hasFormatting) || needFullCleanup.has(/** @type {YText} */ (item.parent))) { return } - const parent = /** @type {YText} */ (item.parent) + const parent = /** @type {YText} */ (item.parent) if (item.content.constructor === ContentFormat) { needFullCleanup.add(parent) } else { @@ -588,12 +588,13 @@ const deleteText = (transaction, currPos, length) => { */ /** - * @extends YEvent + * @template {{ [key:string]: any } | AbstractType } TextEmbeds + * @extends YEvent> * Event that describes the changes on a YText type. */ export class YTextEvent extends YEvent { /** - * @param {YText} ytext + * @param {YText} ytext * @param {Transaction} transaction * @param {Set} subs The keys that changed */ @@ -620,12 +621,12 @@ export class YTextEvent extends YEvent { } /** - * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} + * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} */ get changes () { if (this._changes === null) { /** - * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} + * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} */ const changes = { keys: this.keys, @@ -642,7 +643,7 @@ export class YTextEvent extends YEvent { * Compute the changes in the delta format. * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. * - * @type {delta.TextDelta} + * @type {delta.TextDelta} * * @public */ @@ -754,6 +755,7 @@ export class YTextEvent extends YEvent { * block formats (format information on a paragraph), embeds (complex elements * like pictures and videos), and text formats (**bold**, *italic*). * + * @template {any} Embeds * @extends AbstractType */ export class YText extends AbstractType { @@ -811,7 +813,7 @@ export class YText extends AbstractType { * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {YText} + * @return {YText} */ clone () { const text = new YText() @@ -916,14 +918,14 @@ export class YText extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. + * @return {import('../utils/Delta.js').TextDelta< Embeds extends import('./AbstractType.js').AbstractType ? import('./AbstractType.js').DeepContent : Embeds >} The Delta representation of this type. * * @public */ getContentDeep (am = noAttributionsManager) { return this.getContent(am).map(d => - d instanceof delta.InsertStringOp && d.insert instanceof AbstractType - ? new delta.InsertStringOp(d.insert.getContent(am), d.attributes, d.attribution) + d instanceof delta.InsertEmbedOp && d.insert instanceof AbstractType + ? new delta.InsertEmbedOp(d.insert.getContent(am), d.attributes, d.attribution) : d ) } @@ -936,7 +938,7 @@ export class YText extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * * @public */ diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 48104a462..ecdf61e73 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -271,8 +271,9 @@ export class SnapshotAttributionManager { const inserts = createIdMap() const deletes = createIdMapFromIdSet(diffIdSet(nextSnapshot.ds, prevSnapshot.ds), [createAttributionItem('change', '')]) nextSnapshot.sv.forEach((clock, client) => { - inserts.add(client, 0, prevSnapshot.sv.get(client) || 0, []) - inserts.add(client, prevSnapshot.sv.get(client) || 0, clock, [createAttributionItem('change', '')]) + const prevClock = prevSnapshot.sv.get(client) || 0 + inserts.add(client, 0, prevClock, []) // content is included in prevSnapshot is rendered without attributes + inserts.add(client, prevClock, clock - prevClock, [createAttributionItem('change', '')]) // content is rendered as "inserted" }) this.attrs = mergeIdMaps([diffIdMap(inserts, prevSnapshot.ds), deletes]) } @@ -289,10 +290,12 @@ export class SnapshotAttributionManager { let content = slice.length === 1 ? item.content : item.content.copy() slice.forEach(s => { const deleted = this.nextSnapshot.ds.has(item.id.client, s.clock) + const nonExistend = (this.nextSnapshot.sv.get(item.id.client) ?? 0) <= s.clock const c = content if (s.len < c.getLength()) { content = c.splice(s.len) } + if (nonExistend) return if (!deleted || (s.attrs != null && s.attrs.length > 0)) { let attrsWithoutChange = s.attrs?.filter(attr => attr.name !== 'change') ?? null if (s.attrs?.length === 0) { diff --git a/src/utils/Delta.js b/src/utils/Delta.js index dace41f7b..f8ff00893 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -192,10 +192,6 @@ export class RetainOp { } } -/** - * @typedef {string | { [key: string]: any }} TextDeltaContent - */ - /** * @typedef {(TextDelta | ArrayDelta)} Delta */ diff --git a/tests/testHelper.js b/tests/testHelper.js index a84cd3c8d..ee55830cf 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -465,7 +465,7 @@ export const compare = users => { const userArrayValues = users.map(u => u.getArray('array').toJSON()) const userMapValues = users.map(u => u.getMap('map').toJSON()) const userXmlValues = users.map(u => u.get('xml', Y.XmlElement).toString()) - const userTextValues = users.map(u => u.getText('text').getContent()) + const userTextValues = users.map(u => u.getText('text').getContentDeep()) for (const u of users) { t.assert(u.store.pendingDs === null) t.assert(u.store.pendingStructs === null) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index b37cc4595..acd4f2864 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1872,7 +1872,7 @@ export const testSnapshotDeleteAfter = tc => { }, { insert: 'e' }]) - const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot1)) + const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) t.compare(state1, delta.fromJSON([{ insert: 'abcd' }])) } From fc620617df44ca99ac326a38be191a5fb8832015 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 7 May 2025 22:43:23 +0200 Subject: [PATCH 308/362] lint & fix tests --- src/structs/ContentFormat.js | 14 +++++++------- src/types/AbstractType.js | 4 ++-- src/types/YText.js | 13 +++++++++++-- src/utils/AttributionManager.js | 3 +-- src/utils/Delta.js | 21 ++++++++++++--------- src/utils/YEvent.js | 5 +++-- tests/y-array.tests.js | 2 +- tests/y-text.tests.js | 10 +++++----- tests/y-xml.tests.js | 18 +++++++++--------- 9 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/structs/ContentFormat.js b/src/structs/ContentFormat.js index eb2bd0eed..631e77559 100644 --- a/src/structs/ContentFormat.js +++ b/src/structs/ContentFormat.js @@ -67,24 +67,24 @@ export class ContentFormat { */ integrate (_transaction, item) { // @todo searchmarker are currently unsupported for rich text documents - const p = /** @type {YText} */ (item.parent) + const p = /** @type {YText} */ (item.parent) p._searchMarker = null p._hasFormatting = true } /** - * @param {Transaction} transaction + * @param {Transaction} _transaction */ - delete (transaction) {} + delete (_transaction) {} /** - * @param {StructStore} store + * @param {StructStore} _store */ - gc (store) {} + gc (_store) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder - * @param {number} offset + * @param {number} _offset */ - write (encoder, offset) { + write (encoder, _offset) { encoder.writeKey(this.key) encoder.writeJSON(this.value) } diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index b9b1b44f2..ac81ec7e8 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -22,10 +22,10 @@ import * as math from 'lib0/math' import * as log from 'lib0/logging' /** - * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution } } }} YXmlDeepContent + * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution } } }} YXmlDeepContent */ /** - * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution} } }} DeepContent + * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution} } }} DeepContent */ /** diff --git a/src/types/YText.js b/src/types/YText.js index 794912790..f556bcba4 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -755,8 +755,8 @@ export class YTextEvent extends YEvent { * block formats (format information on a paragraph), embeds (complex elements * like pictures and videos), and text formats (**bold**, *italic*). * - * @template {any} Embeds - * @extends AbstractType + * @template {{ [key:string]:any } | AbstractType} [Embeds={ [key:string]:any } | AbstractType] + * @extends AbstractType> */ export class YText extends AbstractType { /** @@ -804,6 +804,9 @@ export class YText extends AbstractType { this._pending = null } + /** + * @return {YText} + */ _copy () { return new YText() } @@ -816,6 +819,9 @@ export class YText extends AbstractType { * @return {YText} */ clone () { + /** + * @type {YText} + */ const text = new YText() text.applyDelta(this.getContent()) return text @@ -944,6 +950,9 @@ export class YText extends AbstractType { */ getContent (am = noAttributionsManager) { this.doc ?? warnPrematureAccess() + /** + * @type {delta.TextDelta} + */ const d = delta.createTextDelta() /** * @type {Array>} diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index ecdf61e73..8f10ece02 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -11,8 +11,7 @@ import { diffIdMap, createIdMap, createAttributionItem, - mergeIdMaps, - AttributionItem + mergeIdMaps } from '../internals.js' import * as error from 'lib0/error' diff --git a/src/utils/Delta.js b/src/utils/Delta.js index f8ff00893..d7a9b8a54 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -5,17 +5,17 @@ import * as error from 'lib0/error' /** * @template {any} ArrayContent - * @template {{[key: string]: any}} Embeds + * @template {object} Embeds * @typedef {InsertStringOp|InsertEmbedOp|InsertArrayOp|RetainOp|DeleteOp} DeltaOp */ /** - * @template {{[key: string]: any}} Embeds + * @template {object} Embeds * @typedef {InsertStringOp|InsertEmbedOp|RetainOp|DeleteOp} TextDeltaOp */ /** - * @template {any} ArrayContent + * @template ArrayContent * @typedef {InsertArrayOp|RetainOp|DeleteOp} ArrayDeltaOp */ @@ -101,7 +101,7 @@ export class InsertArrayOp { } /** - * @template {{[key: string]: any}} Embeds + * @template {object} Embeds */ export class InsertEmbedOp { /** @@ -274,8 +274,8 @@ const mergeAttrs = (a, b) => { } /** - * @template {'array' | 'text' | 'custom'} [Type='custom'] - * @template {DeltaOp} [TDeltaOp=DeltaOp] + * @template {'array' | 'text' | 'custom'} Type + * @template {DeltaOp} TDeltaOp * @extends AbstractDelta */ export class DeltaBuilder extends AbstractDelta { @@ -353,7 +353,7 @@ export class DeltaBuilder extends AbstractDelta { } /** - * @param {(TDeltaOp extends TextDelta ? string | Embeds : never) | (TDeltaOp extends InsertArrayOp ? Array : never) } insert + * @param {(TDeltaOp extends InsertStringOp ? string : never) | (TDeltaOp extends InsertEmbedOp ? (Embeds) : never) | (TDeltaOp extends InsertArrayOp ? Array : never) } insert * @param {FormattingAttributes?} attributes * @param {Attribution?} attribution * @return {this} @@ -362,6 +362,7 @@ export class DeltaBuilder extends AbstractDelta { const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) if (((this.lastOp instanceof InsertStringOp && insert.constructor === String) || (this.lastOp instanceof InsertArrayOp && insert.constructor === Array)) && (mergedAttributes === this.lastOp.attributes || fun.equalityDeep(mergedAttributes, this.lastOp.attributes)) && (mergedAttribution === this.lastOp.attribution || fun.equalityDeep(mergedAttribution, this.lastOp.attribution))) { + // @ts-ignore if (insert.constructor === String) { // @ts-ignore this.lastOp.insert += insert @@ -441,12 +442,14 @@ export class TextDelta extends DeltaBuilder { } /** - * @return {TextDelta} + * @template {object} Embeds + * @return {TextDelta} */ export const createTextDelta = () => new TextDelta() /** - * @return {ArrayDelta} + * @template [V=any] + * @return {ArrayDelta} */ export const createArrayDelta = () => new ArrayDelta() diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index b7b8ebe39..4a49ec902 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -7,7 +7,8 @@ import * as array from 'lib0/array' import * as error from 'lib0/error' /** - * @typedef {import('../utils/Delta.js').TextDelta} TextDelta + * @template {object} Embed + * @typedef {import('../utils/Delta.js').TextDelta} TextDelta */ /** @@ -50,7 +51,7 @@ export class YEvent { */ this._keys = null /** - * @type {TextDelta?} + * @type {TextDelta?} */ this._delta = null /** diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index faad66c4c..4eaab5d2d 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -530,7 +530,7 @@ export const testAttributedContent = _tc => { }) const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) const attributedContent = yarray.getContent(attributionManager) - console.log(attributedContent.toJSON().ops) + console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index acd4f2864..b06072791 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1804,7 +1804,7 @@ export const testTypesAsEmbed = tc => { text0.applyDelta([{ insert: new Y.Map([['key', 'val']]) }]) - t.compare(/** @type {delta.InsertOp} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' }) + t.compare(/** @type {delta.InsertEmbedOp} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' }) let firedEvent = false text1.observe(event => { const d = event.delta @@ -1944,7 +1944,7 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { }) testConnector.flushAllMessages() /** - * @type {Array} + * @type {Array>} */ const deltas = [] text0.observe(event => { @@ -2262,14 +2262,14 @@ export const testAttributedContent = _tc => { ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') const attributedContent = ytext.getContent(attributionManager) - console.log(attributedContent.toJSON().ops) + console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) t.group('unformat', () => { ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') const attributedContent = ytext.getContent(attributionManager) - console.log(attributedContent.toJSON().ops) + console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) } @@ -2300,7 +2300,7 @@ export const testAttributedDiffing = _tc => { const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions) // we render the attributed content with the attributionManager const attributedContent = ytext.getContent(attributionManager) - console.log(JSON.stringify(attributedContent.toJSON().ops, null, 2)) + console.log(JSON.stringify(attributedContent.toJSON(), null, 2)) const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') t.assert(attributedContent.equals(expectedContent)) console.log(Y.encodeIdMap(attributedInsertions).length) diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 251a0c384..a0c0611ca 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -245,7 +245,7 @@ export const testFragmentAttributedContent = _tc => { }) const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) const attributedContent = yfragment.getContent(attributionManager) - console.log(attributedContent.children.toJSON().ops) + console.log(attributedContent.children.toJSON()) t.assert(attributedContent.children.equals(expectedContent)) t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) }) @@ -274,7 +274,7 @@ export const testElementAttributedContent = _tc => { }) const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) const attributedContent = yelement.getContent(attributionManager) - console.log('children', attributedContent.children.toJSON().ops) + console.log('children', attributedContent.children.toJSON()) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) @@ -288,8 +288,8 @@ export const testElementAttributedContent = _tc => { delta.createTextDelta().insert('world', null, { insert: [] }) ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) - console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) @@ -317,7 +317,7 @@ export const testElementAttributedContentViaDiffer = _tc => { const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello', null, { delete: [] })], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', attributedContent.children.toJSON().ops) + console.log('children', attributedContent.children.toJSON()) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) @@ -331,8 +331,8 @@ export const testElementAttributedContentViaDiffer = _tc => { delta.createTextDelta().insert('world', null, { insert: [] }) ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) - console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) @@ -352,8 +352,8 @@ export const testElementAttributedContentViaDiffer = _tc => { delta.createTextDelta().insert('bigworld', null, { insert: [] }) ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2)) - console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2)) + console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) From 62422544bc343a5ea11594ba2a2a59256e9bef2a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 8 May 2025 15:18:18 +0200 Subject: [PATCH 309/362] events can be computed with attributions --- src/types/AbstractType.js | 6 +- src/types/YText.js | 104 +++++++++++++++++++------------- src/utils/AttributionManager.js | 23 ++++--- src/utils/Delta.js | 15 ++++- 4 files changed, 91 insertions(+), 57 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index ac81ec7e8..31e9acc66 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -514,7 +514,7 @@ export const typeListGetContent = (type, am) => { for (let item = type._start; item !== null; cs.length = 0) { // populate cs for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item) + am.readContent(cs, item, false) } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] @@ -1005,7 +1005,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const cs = [] - am.readContent(cs, item) + am.readContent(cs, item, false) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) const attribution = createAttributionFromAttributionItems(attrs, deleted) @@ -1021,7 +1021,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const tmpcs = [] - am.readContent(tmpcs, prevItem) + am.readContent(tmpcs, prevItem, false) cs = tmpcs.concat(cs) if (cs.length === 0 || cs[0].attrs == null) { cs.splice(0, cs.findIndex(c => c.attrs != null)) diff --git a/src/types/YText.js b/src/types/YText.js index f556bcba4..22c732794 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -640,47 +640,55 @@ export class YTextEvent extends YEvent { } /** - * Compute the changes in the delta format. - * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. - * - * @type {delta.TextDelta} + * @param {AbstractAttributionManager} am + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * * @public */ - get delta () { - if (this._delta === null) { - const ydoc = /** @type {Doc} */ (this.target.doc) - const d = this._delta = delta.createTextDelta() - transact(ydoc, transaction => { - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - let currentAttributes = {} // saves all current attributes for insert - let usingCurrentAttributes = false - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - let changedAttributes = {} // saves changed attributes for retain - let usingChangedAttributes = false - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - const previousAttributes = {} // The value before changes - const tr = this.transaction - let item = this.target._start - while (item !== null) { - const freshDelete = item.deleted && tr.deleteSet.hasId(item.id) && !tr.insertSet.hasId(item.id) - const freshInsert = !item.deleted && tr.insertSet.hasId(item.id) - switch (item.content.constructor) { + getDelta (am = noAttributionsManager) { + const ydoc = /** @type {Doc} */ (this.target.doc) + /** + * @type {import('../utils/Delta.js').TextDelta} + */ + const d = delta.createTextDelta() + transact(ydoc, transaction => { + /** + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + let currentAttributes = {} // saves all current attributes for insert + let usingCurrentAttributes = false + /** + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + let changedAttributes = {} // saves changed attributes for retain + let usingChangedAttributes = false + /** + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + const previousAttributes = {} // The value before changes + const tr = this.transaction + + /** + * @type {Array>} + */ + const cs = [] + for (let item = this.target._start; item !== null; cs.length = 0, item = item.right) { + const freshDelete = item.deleted && tr.deleteSet.hasId(item.id) && !tr.insertSet.hasId(item.id) + const freshInsert = !item.deleted && tr.insertSet.hasId(item.id) + am.readContent(cs, item, freshDelete) // do item.right after calling this + for (let i = 0; i < cs.length; i++) { + const c = cs[i] + const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted) + switch (c.content.constructor) { case ContentType: case ContentEmbed: if (freshInsert) { d.usedAttributes = currentAttributes usingCurrentAttributes = true - d.insert(item.content.getContent()[0]) + d.insert(c.content.getContent()[0], null, attribution) } else if (freshDelete) { d.delete(1) - } else if (!item.deleted) { + } else if (!c.deleted) { d.usedAttributes = changedAttributes usingChangedAttributes = true d.retain(1) @@ -690,17 +698,17 @@ export class YTextEvent extends YEvent { if (freshInsert) { d.usedAttributes = currentAttributes usingCurrentAttributes = true - d.insert(/** @type {ContentString} */ (item.content).str) + d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) } else if (freshDelete) { - d.delete(item.length) - } else if (!item.deleted) { + d.delete(c.content.getLength()) + } else if (!c.deleted) { d.usedAttributes = changedAttributes usingChangedAttributes = true - d.retain(item.length) + d.retain(c.content.getLength()) } break case ContentFormat: { - const { key, value } = /** @type {ContentFormat} */ (item.content) + const { key, value } = /** @type {ContentFormat} */ (c.content) const currAttrVal = currentAttributes[key] ?? null if (freshDelete || freshInsert) { // create fresh references @@ -727,7 +735,7 @@ export class YTextEvent extends YEvent { changedAttributes[key] = currAttrVal currentAttributes[key] = currAttrVal previousAttributes[key] = value - } else if (!item.deleted) { + } else if (!c.deleted) { // fresh reference to currentAttributes only if (usingCurrentAttributes) { currentAttributes = object.assign({}, currentAttributes) @@ -739,12 +747,22 @@ export class YTextEvent extends YEvent { break } } - item = item.right } - }) - d.done() - } - return /** @type {any} */ (this._delta) + } + }) + return d.done() + } + + /** + * Compute the changes in the delta format. + * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. + * + * @type {delta.TextDelta} + * + * @public + */ + get delta () { + return this._delta ?? (this._delta = this.getDelta()) } } @@ -961,7 +979,7 @@ export class YText extends AbstractType { for (let item = this._start; item !== null; cs.length = 0) { // populate cs for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item) + am.readContent(cs, item, false) } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 8f10ece02..cecadb5c5 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -89,8 +89,9 @@ export class AbstractAttributionManager { /** * @param {Array>} _contents * @param {Item} _item + * @param {boolean} _forceRead read content even if it is unattributed and deleted */ - readContent (_contents, _item) { + readContent (_contents, _item, _forceRead) { error.methodUnimplemented() } @@ -115,8 +116,9 @@ export class TwosetAttributionManager { /** * @param {Array>} contents * @param {Item} item + * @param {boolean} forceRead read content even if it is unattributed and deleted */ - readContent (contents, item) { + readContent (contents, item, forceRead) { const deleted = item.deleted const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() @@ -125,7 +127,7 @@ export class TwosetAttributionManager { if (s.len < c.getLength()) { content = c.splice(s.len) } - if (!deleted || s.attrs != null) { + if (!deleted || s.attrs != null || forceRead) { contents.push(new AttributedContent(c, deleted, s.attrs)) } }) @@ -143,9 +145,10 @@ export class NoAttributionsManager { /** * @param {Array>} contents * @param {Item} item + * @param {boolean} forceRead read content even if it is unattributed and deleted */ - readContent (contents, item) { - if (!item.deleted) { + readContent (contents, item, forceRead) { + if (!item.deleted || forceRead) { contents.push(new AttributedContent(item.content, false, null)) } } @@ -214,8 +217,9 @@ export class DiffAttributionManager { /** * @param {Array>} contents * @param {Item} item + * @param {boolean} forceRead read content even if it is unattributed and deleted */ - readContent (contents, item) { + readContent (contents, item, forceRead) { const deleted = item.deleted || /** @type {any} */ (item.parent).doc !== this._nextDoc const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() @@ -238,7 +242,7 @@ export class DiffAttributionManager { if (s.len < c.getLength()) { content = c.splice(s.len) } - if (!deleted || s.attrs != null) { + if (!deleted || s.attrs != null || forceRead) { contents.push(new AttributedContent(c, deleted, s.attrs)) } }) @@ -282,8 +286,9 @@ export class SnapshotAttributionManager { /** * @param {Array>} contents * @param {Item} item + * @param {boolean} forceRead read content even if it is unattributed and deleted */ - readContent (contents, item) { + readContent (contents, item, forceRead) { if ((this.nextSnapshot.sv.get(item.id.client) ?? 0) <= item.id.clock) return // future item that should not be displayed const slice = this.attrs.slice(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() @@ -295,7 +300,7 @@ export class SnapshotAttributionManager { content = c.splice(s.len) } if (nonExistend) return - if (!deleted || (s.attrs != null && s.attrs.length > 0)) { + if (!deleted || forceRead || (s.attrs != null && s.attrs.length > 0)) { let attrsWithoutChange = s.attrs?.filter(attr => attr.name !== 'change') ?? null if (s.attrs?.length === 0) { attrsWithoutChange = null diff --git a/src/utils/Delta.js b/src/utils/Delta.js index d7a9b8a54..6f820667a 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -410,7 +410,7 @@ export class DeltaBuilder extends AbstractDelta { } /** - * @return {AbstractDelta} + * @return {this} */ done () { while (this.lastOp != null && this.lastOp instanceof RetainOp && this.lastOp.attributes === null) { @@ -432,7 +432,7 @@ export class ArrayDelta extends DeltaBuilder { } /** - * @template {{ [key:string]: any }} Embeds + * @template {object} Embeds * @extends DeltaBuilder<'text',TextDeltaOp> */ export class TextDelta extends DeltaBuilder { @@ -441,6 +441,17 @@ export class TextDelta extends DeltaBuilder { } } +/** + * @template {'text'|'array'|'custom'} Type + * @template {DeltaOp} DeltaOps + * @typedef {AbstractDelta} DeltaReadonly + */ + +/** + * @template {object} Embeds + * @typedef {DeltaReadonly<'text',TextDeltaOp>} TextDeltaReadonly + */ + /** * @template {object} Embeds * @return {TextDelta} From b646654df113ec92dc95be09d66871e09eb1b7e3 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 9 May 2025 20:34:18 +0200 Subject: [PATCH 310/362] be able to intersect idmaps and idsets --- src/types/AbstractType.js | 10 ++- src/types/YText.js | 18 ++-- src/utils/AttributionManager.js | 14 ++-- src/utils/IdMap.js | 39 ++++++--- src/utils/IdSet.js | 144 +++++++++++++++++++++++++++++++- tests/IdMap.tests.js | 35 +++++++- tests/IdSet.tests.js | 19 +++++ 7 files changed, 252 insertions(+), 27 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 31e9acc66..c7512a0de 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -518,8 +518,12 @@ export const typeListGetContent = (type, am) => { } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] - const attribution = createAttributionFromAttributionItems(attrs, deleted) - d.insert(content.getContent(), null, attribution) + const { attribution, retainOnly } = createAttributionFromAttributionItems(attrs, deleted) + if (retainOnly) { + d.retain(content.getLength()) + } else if (content.isCountable()) { + d.insert(content.getContent(), null, attribution) + } } } return d @@ -1008,7 +1012,7 @@ export const typeMapGetContent = (parent, am) => { am.readContent(cs, item, false) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) - const attribution = createAttributionFromAttributionItems(attrs, deleted) + const { attribution } = createAttributionFromAttributionItems(attrs, deleted) if (deleted) { mapcontent[key] = { prevValue: c, value: undefined, attribution } } else { diff --git a/src/types/YText.js b/src/types/YText.js index 22c732794..d2740e1b9 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -24,7 +24,7 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line + IdSet, noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line createAttributionFromAttributionItems } from '../internals.js' @@ -678,7 +678,7 @@ export class YTextEvent extends YEvent { am.readContent(cs, item, freshDelete) // do item.right after calling this for (let i = 0; i < cs.length; i++) { const c = cs[i] - const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted) + const { attribution } = createAttributionFromAttributionItems(c.attrs, c.deleted) switch (c.content.constructor) { case ContentType: case ContentEmbed: @@ -983,15 +983,23 @@ export class YText extends AbstractType { } for (let i = 0; i < cs.length; i++) { const { content, deleted, attrs } = cs[i] - const attribution = createAttributionFromAttributionItems(attrs, deleted) + const { attribution, retainOnly } = createAttributionFromAttributionItems(attrs, deleted) switch (content.constructor) { case ContentString: { - d.insert(/** @type {ContentString} */ (content).str, null, attribution) + if (retainOnly) { + d.retain(content.getLength(), null, attribution) + } else { + d.insert(/** @type {ContentString} */ (content).str, null, attribution) + } break } case ContentType: case ContentEmbed: { - d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution) + if (retainOnly) { + d.retain(content.getLength(), null, attribution) + } else { + d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution) + } break } case ContentFormat: { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index cecadb5c5..3326e5944 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -31,13 +31,14 @@ import * as error from 'lib0/error' /** * @param {Array>?} attrs * @param {boolean} deleted - whether the attributed item is deleted - * @return {Attribution?} + * @return {{ attribution: Attribution?, retainOnly: boolean }} */ export const createAttributionFromAttributionItems = (attrs, deleted) => { /** * @type {Attribution?} */ let attribution = null + let retainOnly = false if (attrs != null) { attribution = {} if (deleted) { @@ -47,6 +48,9 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { } attrs.forEach(attr => { switch (attr.name) { + case 'retain': + retainOnly = true + break case 'insert': case 'delete': case 'suggest': { @@ -63,7 +67,7 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { } }) } - return attribution + return { attribution, retainOnly } } /** @@ -120,7 +124,7 @@ export class TwosetAttributionManager { */ readContent (contents, item, forceRead) { const deleted = item.deleted - const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) + const slice = (deleted ? this.deletes : this.inserts).sliceId(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() slice.forEach(s => { const c = content @@ -221,7 +225,7 @@ export class DiffAttributionManager { */ readContent (contents, item, forceRead) { const deleted = item.deleted || /** @type {any} */ (item.parent).doc !== this._nextDoc - const slice = (deleted ? this.deletes : this.inserts).slice(item.id, item.length) + const slice = (deleted ? this.deletes : this.inserts).sliceId(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.hasId(item.id)) { // Retrieved item is never more fragmented than the newer item. @@ -290,7 +294,7 @@ export class SnapshotAttributionManager { */ readContent (contents, item, forceRead) { if ((this.nextSnapshot.sv.get(item.id.client) ?? 0) <= item.id.clock) return // future item that should not be displayed - const slice = this.attrs.slice(item.id, item.length) + const slice = this.attrs.sliceId(item.id, item.length) let content = slice.length === 1 ? item.content : item.content.copy() slice.forEach(s => { const deleted = this.nextSnapshot.ds.has(item.id.client, s.clock) diff --git a/src/utils/IdMap.js b/src/utils/IdMap.js index f8c684920..e9fff56b7 100644 --- a/src/utils/IdMap.js +++ b/src/utils/IdMap.js @@ -4,7 +4,8 @@ import { findRangeStartInIdRanges, _deleteRangeFromIdSet, DSDecoderV1, DSDecoderV2, IdSetEncoderV1, IdSetEncoderV2, IdSet, ID, // eslint-disable-line - _insertIntoIdSet + _insertIntoIdSet, + _intersectSets } from '../internals.js' import * as array from 'lib0/array' @@ -360,8 +361,20 @@ export class IdMap { * @param {number} len * @return {Array>} */ - slice (id, len) { - const dr = this.clients.get(id.client) + sliceId (id, len) { + return this.slice(id.client, id.clock, len) + } + + /** + * Return attributions for a slice of ids. + * + * @param {number} client + * @param {number} clock + * @param {number} len + * @return {Array>} + */ + slice (client, clock, len) { + const dr = this.clients.get(client) /** * @type {Array>} */ @@ -371,19 +384,19 @@ export class IdMap { * @type {Array>} */ const ranges = dr.getIds() - let index = findRangeStartInIdRanges(ranges, id.clock) + let index = findRangeStartInIdRanges(ranges, clock) if (index !== null) { let prev = null while (index < ranges.length) { let r = ranges[index] - if (r.clock < id.clock) { - r = new AttrRange(id.clock, r.len - (id.clock - r.clock), r.attrs) + if (r.clock < clock) { + r = new AttrRange(clock, r.len - (clock - r.clock), r.attrs) } - if (r.clock + r.len > id.clock + len) { - r = new AttrRange(r.clock, id.clock + len - r.clock, r.attrs) + if (r.clock + r.len > clock + len) { + r = new AttrRange(r.clock, clock + len - r.clock, r.attrs) } if (r.len <= 0) break - const prevEnd = prev != null ? prev.clock + prev.len : id.clock + const prevEnd = prev != null ? prev.clock + prev.len : clock if (prevEnd < r.clock) { res.push(createMaybeAttrRange(prevEnd, r.clock - prevEnd, null)) } @@ -396,11 +409,11 @@ export class IdMap { if (res.length > 0) { const last = res[res.length - 1] const end = last.clock + last.len - if (end < id.clock + len) { - res.push(createMaybeAttrRange(end, id.clock + len - end, null)) + if (end < clock + len) { + res.push(createMaybeAttrRange(end, clock + len - end, null)) } } else { - res.push(createMaybeAttrRange(id.clock, len, null)) + res.push(createMaybeAttrRange(clock, len, null)) } return res } @@ -610,3 +623,5 @@ export const diffIdMap = (set, exclude) => { diffed.attrsH = set.attrsH return diffed } + +export const intersectMaps = _intersectSets diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 5e236b738..097cb1544 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -6,7 +6,8 @@ import { UpdateEncoderV2, IdMap, AttrRanges, - AbstractStruct, DSDecoderV1, IdSetEncoderV1, DSDecoderV2, IdSetEncoderV2, Item, GC, StructStore, Transaction, ID // eslint-disable-line + AttrRange, + AbstractStruct, DSDecoderV1, IdSetEncoderV1, DSDecoderV2, IdSetEncoderV2, Item, GC, StructStore, Transaction, ID, AttributionItem, // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' @@ -37,8 +38,47 @@ export class IdRange { copyWith (clock, len) { return new IdRange(clock, len) } + + /** + * Helper method making this compatible with IdMap. + * + * @return {Array>} + */ + get attrs () { + return [] + } +} + +export class MaybeIdRange { + /** + * @param {number} clock + * @param {number} len + * @param {boolean} exists + */ + constructor (clock, len, exists) { + /** + * @type {number} + */ + this.clock = clock + /** + * @type {number} + */ + this.len = len + /** + * @type {boolean} + */ + this.exists = exists + } } +/** + * @param {number} clock + * @param {number} len + * @param {boolean} exists + * @return {MaybeIdRange} + */ +export const createMaybeIdRange = (clock, len, exists) => new MaybeIdRange(clock, len, exists) + class IdRanges { /** * @param {Array} ids @@ -144,6 +184,59 @@ export class IdSet { return false } + /** + * Return slices of ids that exist in this idset. + * + * @param {number} client + * @param {number} clock + * @param {number} len + * @return {Array} + */ + slice (client, clock, len) { + const dr = this.clients.get(client) + /** + * @type {Array} + */ + const res = [] + if (dr) { + /** + * @type {Array} + */ + const ranges = dr.getIds() + let index = findRangeStartInIdRanges(ranges, clock) + if (index !== null) { + let prev = null + while (index < ranges.length) { + let r = ranges[index] + if (r.clock < clock) { + r = new IdRange(clock, r.len - (clock - r.clock)) + } + if (r.clock + r.len > clock + len) { + r = new IdRange(r.clock, clock + len - r.clock) + } + if (r.len <= 0) break + const prevEnd = prev != null ? prev.clock + prev.len : clock + if (prevEnd < r.clock) { + res.push(createMaybeIdRange(prevEnd, r.clock - prevEnd, false)) + } + prev = r + res.push(createMaybeIdRange(r.clock, r.len, true)) + index++ + } + } + } + if (res.length > 0) { + const last = res[res.length - 1] + const end = last.clock + last.len + if (end < clock + len) { + res.push(createMaybeIdRange(end, clock + len - end, false)) + } + } else { + res.push(createMaybeIdRange(clock, len, false)) + } + return res + } + /** * @param {number} client * @param {number} clock @@ -399,6 +492,55 @@ export const _diffSet = (set, exclude) => { */ export const diffIdSet = _diffSet +/** + * @template {IdSet | IdMap} SetA + * @template {IdSet | IdMap} SetB + * @param {SetA} setA + * @param {SetB} setB + * @return {SetA extends IdMap ? (SetB extends IdMap ? IdMap : IdMap) : IdSet} + */ +export const _intersectSets = (setA, setB) => { + /** + * @type {IdMap | IdSet} + */ + const res = /** @type {any } */ (setA instanceof IdSet ? new IdSet() : new IdMap()) + const Ranges = setA instanceof IdSet ? IdRanges : AttrRanges + setA.clients.forEach((_aRanges, client) => { + /** + * @type {Array} + */ + const resRanges = [] + const _bRanges = setB.clients.get(client) + const aRanges = _aRanges.getIds() + if (_bRanges != null) { + const bRanges = _bRanges.getIds() + for (let a = 0, b = 0; a < aRanges.length && b < bRanges.length;) { + const aRange = aRanges[a] + const bRange = bRanges[b] + // construct overlap + const clock = math.max(aRange.clock, bRange.clock) + const len = math.min(aRange.len - (clock - aRange.clock), bRange.len - (clock - bRange.clock)) + if (len > 0) { + resRanges.push(aRange instanceof AttrRange + ? new AttrRange(clock, len, /** @type {Array>} */ (aRange.attrs).concat(bRange.attrs)) + : new IdRange(clock, len) + ) + } + if (aRange.clock + aRange.len < bRange.clock + bRange.len) { + a++ + } else { + b++ + } + } + } + // @ts-ignore + if (resRanges.length > 0) res.clients.set(client, /** @type {any} */ (new Ranges(resRanges))) + }) + return /** @type {any} */ (res) +} + +export const intersectSets = _intersectSets + /** * @param {IdSet} idSet * @param {number} client diff --git a/tests/IdMap.tests.js b/tests/IdMap.tests.js index 54d2cbcb2..38db22e88 100644 --- a/tests/IdMap.tests.js +++ b/tests/IdMap.tests.js @@ -94,7 +94,7 @@ export const testRepeatMergingMultipleIdMaps = tc => { const mergedHas = merged.hasId(new ID(iclient, iclock)) const oneHas = sets.some(ids => ids.hasId(new ID(iclient, iclock))) t.assert(mergedHas === oneHas) - const mergedAttrs = merged.slice(new ID(iclient, iclock), 1) + const mergedAttrs = merged.sliceId(new ID(iclient, iclock), 1) mergedAttrs.forEach(a => { if (a.attrs != null) { composed.add(iclient, a.clock, a.len, a.attrs) @@ -161,3 +161,36 @@ export const testRepeatRandomDeletes = tc => { } compareIdMaps(idset, diffed) } + +/** + * @param {t.TestCase} tc + */ +export const testrepeatRandomIntersects = tc => { + const clients = 4 + const clockRange = 100 + const ids1 = createRandomIdMap(tc.prng, clients, clockRange, [1]) + const ids2 = createRandomIdMap(tc.prng, clients, clockRange, ['two']) + const intersected = idmap.intersectMaps(ids1, ids2) + for (let client = 0; client < clients; client++) { + for (let clock = 0; clock < clockRange; clock++) { + t.assert((ids1.has(client, clock) && ids2.has(client, clock)) === intersected.has(client, clock)) + /** + * @type {Array?} + */ + const slice1 = ids1.slice(client, clock, 1)[0].attrs + /** + * @type {Array?} + */ + const slice2 = ids2.slice(client, clock, 1)[0].attrs + /** + * @type {Array?} + */ + const expectedAttrs = (slice1 != null && slice2 != null) ? slice1.concat(slice2) : null + const attrs = intersected.slice(client, clock, 1)[0].attrs + t.assert(attrs?.length === expectedAttrs?.length) + } + } + const diffed1 = idmap.diffIdMap(ids1, ids2) + const altDiffed1 = idmap.diffIdMap(ids1, intersected) + compareIdMaps(diffed1, altDiffed1) +} diff --git a/tests/IdSet.tests.js b/tests/IdSet.tests.js index d55ba352a..33ad50c51 100644 --- a/tests/IdSet.tests.js +++ b/tests/IdSet.tests.js @@ -221,3 +221,22 @@ export const testRepeatRandomDiffing2 = tc => { const excludedMerged = d.mergeIdSets([e1, e2]) compareIdSets(mergedExcluded, excludedMerged) } + +/** + * @param {t.TestCase} tc + */ +export const testrepeatRandomIntersects = tc => { + const clients = 4 + const clockRange = 100 + const ids1 = createRandomIdSet(tc.prng, clients, clockRange) + const ids2 = createRandomIdSet(tc.prng, clients, clockRange) + const intersected = d.intersectSets(ids1, ids2) + for (let client = 0; client < clients; client++) { + for (let clock = 0; clock < clockRange; clock++) { + t.assert((ids1.has(client, clock) && ids2.has(client, clock)) === intersected.has(client, clock)) + } + } + const diffed1 = d.diffIdSet(ids1, ids2) + const altDiffed1 = d.diffIdSet(ids1, intersected) + compareIdSets(diffed1, altDiffed1) +} From 4d582748c19bb349dc05ab7725fb8da894c6eb6e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 18 May 2025 22:56:14 +0200 Subject: [PATCH 311/362] more generic gedDelta implementation (could be used for events) --- src/types/AbstractType.js | 20 ++-- src/types/YText.js | 191 +++++++++++++++++++++++++++++++- src/utils/AttributionManager.js | 109 ++++++++++-------- tests/y-text.tests.js | 2 +- tests/y-xml.tests.js | 7 +- 5 files changed, 268 insertions(+), 61 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index c7512a0de..c1856d345 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -514,15 +514,17 @@ export const typeListGetContent = (type, am) => { for (let item = type._start; item !== null; cs.length = 0) { // populate cs for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item, false) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, true) } for (let i = 0; i < cs.length; i++) { - const { content, deleted, attrs } = cs[i] - const { attribution, retainOnly } = createAttributionFromAttributionItems(attrs, deleted) - if (retainOnly) { - d.retain(content.getLength()) - } else if (content.isCountable()) { - d.insert(content.getContent(), null, attribution) + const c = cs[i] + const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted).attribution + if (c.content.isCountable()) { + if (c.render) { + d.insert(c.content.getContent(), null, attribution) + } else { + d.retain(c.content.getLength()) + } } } } @@ -1009,7 +1011,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const cs = [] - am.readContent(cs, item, false) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, true) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) const { attribution } = createAttributionFromAttributionItems(attrs, deleted) @@ -1025,7 +1027,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const tmpcs = [] - am.readContent(tmpcs, prevItem, false) + am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, true) cs = tmpcs.concat(cs) if (cs.length === 0 || cs[0].attrs == null) { cs.splice(0, cs.findIndex(c => c.attrs != null)) diff --git a/src/types/YText.js b/src/types/YText.js index d2740e1b9..16c37e2a0 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -24,7 +24,7 @@ import { updateMarkerChanges, ContentType, warnPrematureAccess, - IdSet, noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line + noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line createAttributionFromAttributionItems } from '../internals.js' @@ -675,7 +675,7 @@ export class YTextEvent extends YEvent { for (let item = this.target._start; item !== null; cs.length = 0, item = item.right) { const freshDelete = item.deleted && tr.deleteSet.hasId(item.id) && !tr.insertSet.hasId(item.id) const freshInsert = !item.deleted && tr.insertSet.hasId(item.id) - am.readContent(cs, item, freshDelete) // do item.right after calling this + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted || freshDelete) // do item.right after calling this for (let i = 0; i < cs.length; i++) { const c = cs[i] const { attribution } = createAttributionFromAttributionItems(c.attrs, c.deleted) @@ -709,6 +709,7 @@ export class YTextEvent extends YEvent { break case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (c.content) + // # update attributes const currAttrVal = currentAttributes[key] ?? null if (freshDelete || freshInsert) { // create fresh references @@ -744,6 +745,29 @@ export class YTextEvent extends YEvent { currentAttributes[key] = value previousAttributes[key] = value } + // # Update Attributions + if (attribution != null) { + /** + * @type {import('../utils/Delta.js').Attribution} + */ + const formattingAttribution = object.assign({}, d.usedAttribution) + const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + if (value === null) { + delete attributesChanged[key] + } else { + const by = attributesChanged[key] = (attributesChanged[key]?.slice() ?? []) + by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt) formattingAttribution.attributedAt = attributedAt + } + if (object.isEmpty(attributesChanged)) { + d.useAttribution(null) + } else { + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + d.useAttribution(formattingAttribution) + } + } break } } @@ -967,6 +991,7 @@ export class YText extends AbstractType { * @public */ getContent (am = noAttributionsManager) { + return this.getDelta(am) this.doc ?? warnPrematureAccess() /** * @type {delta.TextDelta} @@ -1044,6 +1069,168 @@ export class YText extends AbstractType { return d } + /** + * @param {AbstractAttributionManager} am + * @param {import('../utils/IdSet.js').IdSet?} itemsToRender + * @param {boolean} retainOnly - if true, retain the rendered items with attributes and attributions. + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. + * + * @public + */ + getDelta (am = noAttributionsManager, itemsToRender = null, retainOnly = false) { + /** + * @type {import('../utils/Delta.js').TextDelta} + */ + const d = delta.createTextDelta() + /** + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + let currentAttributes = {} // saves all current attributes for insert + let usingCurrentAttributes = false + /** + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + let changedAttributes = {} // saves changed attributes for retain + let usingChangedAttributes = false + /** + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + const previousAttributes = {} // The value before changes + + /** + * @type {Array>} + */ + const cs = [] + for (let item = this._start; item !== null; cs.length = 0) { + if (itemsToRender != null) { + for (; item !== null && cs.length < 50; item = item.right) { + const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length) + let itemContent = rslice.length > 1 ? item.content.copy() : item.content + for (let ir = 0; ir < rslice.length; ir++) { + const idrange = rslice[ir] + const content = itemContent + if (ir !== rslice.length - 1) { + itemContent.splice(idrange.len) + } + am.readContent(cs, item.id.client, idrange.clock, item.deleted, content, idrange.exists) + } + } + } else { + for (; item !== null && cs.length < 50; item = item.right) { + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, true) + } + } + for (let i = 0; i < cs.length; i++) { + const c = cs[i] + const renderDelete = c.deleted && c.attrs != null && c.render + const renderInsert = !c.deleted && (c.render || c.attrs != null) + const attribution = (renderDelete || renderInsert) ? createAttributionFromAttributionItems(c.attrs, c.deleted).attribution : null + switch (c.content.constructor) { + case ContentType: + case ContentEmbed: + if (renderInsert) { + d.usedAttributes = currentAttributes + usingCurrentAttributes = true + d.insert(c.content.getContent()[0], null, attribution) + } else if (renderDelete) { + d.delete(1) + } else if (!c.deleted) { + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(1) + } + break + case ContentString: + if (renderInsert || (renderDelete && attribution?.delete != null)) { + d.usedAttributes = currentAttributes + usingCurrentAttributes = true + d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) + } else if (renderDelete) { + d.delete(c.content.getLength()) + } else if (!c.deleted) { + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(c.content.getLength()) + } + break + case ContentFormat: { + const { key, value } = /** @type {ContentFormat} */ (c.content) + const currAttrVal = currentAttributes[key] ?? null + // # Update Attributes + if (renderDelete || renderInsert) { + // create fresh references + if (usingCurrentAttributes) { + currentAttributes = object.assign({}, currentAttributes) + usingCurrentAttributes = false + } + if (usingChangedAttributes) { + usingChangedAttributes = false + changedAttributes = object.assign({}, changedAttributes) + } + } + if (renderInsert) { + if (equalAttrs(value, currAttrVal)) { + // item.delete(transaction) + } else if (equalAttrs(value, previousAttributes[key] ?? null)) { + delete currentAttributes[key] + delete changedAttributes[key] + } else { + currentAttributes[key] = value + changedAttributes[key] = value + } + } else if (renderDelete) { + if (equalAttrs(value,currAttrVal)) { + delete changedAttributes[key] + delete currentAttributes[key] + } else { + changedAttributes[key] = currAttrVal + currentAttributes[key] = currAttrVal + } + previousAttributes[key] = value + } else if (!c.deleted) { + // fresh reference to currentAttributes only + if (usingCurrentAttributes) { + currentAttributes = object.assign({}, currentAttributes) + usingCurrentAttributes = false + } + if (equalAttrs(value, previousAttributes[key] ?? null)) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } + previousAttributes[key] = value + } + // # Update Attributions + if (attribution != null) { + /** + * @type {import('../utils/Delta.js').Attribution} + */ + const formattingAttribution = object.assign({}, d.usedAttribution) + const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + if (value === null) { + delete attributesChanged[key] + } else { + const by = attributesChanged[key] = (attributesChanged[key]?.slice() ?? []) + by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt) formattingAttribution.attributedAt = attributedAt + } + if (object.isEmpty(attributesChanged)) { + d.useAttribution(null) + } else { + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + d.useAttribution(formattingAttribution) + } + } + break + } + } + } + } + return d.done() + } + /** * Insert text at a given index. * diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 3326e5944..b266f26de 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -5,13 +5,14 @@ import { createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, - Snapshot, Doc, Item, AbstractContent, IdMap, // eslint-disable-line + Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line insertIntoIdMap, insertIntoIdSet, diffIdMap, createIdMap, createAttributionItem, - mergeIdMaps + mergeIdMaps, + createID } from '../internals.js' import * as error from 'lib0/error' @@ -29,6 +30,7 @@ import * as error from 'lib0/error' */ /** + * @todo SHOULD NOT RETURN AN OBJECT! * @param {Array>?} attrs * @param {boolean} deleted - whether the attributed item is deleted * @return {{ attribution: Attribution?, retainOnly: boolean }} @@ -78,11 +80,13 @@ export class AttributedContent { * @param {AbstractContent} content * @param {boolean} deleted * @param {Array> | null} attrs + * @param {boolean} render */ - constructor (content, deleted, attrs) { + constructor (content, deleted, attrs, render) { this.content = content this.deleted = deleted this.attrs = attrs + this.render = render } } @@ -91,11 +95,14 @@ export class AttributedContent { */ export class AbstractAttributionManager { /** - * @param {Array>} _contents - * @param {Item} _item - * @param {boolean} _forceRead read content even if it is unattributed and deleted + * @param {Array>} _contents - where to write the result + * @param {number} _client + * @param {number} _clock + * @param {boolean} _deleted + * @param {AbstractContent} _content + * @param {boolean} _shouldRender - whether this should render or just result in a `retain` operation */ - readContent (_contents, _item, _forceRead) { + readContent (_contents, _client, _clock, _deleted, _content, _shouldRender) { error.methodUnimplemented() } @@ -118,21 +125,23 @@ export class TwosetAttributionManager { destroy () {} /** - * @param {Array>} contents - * @param {Item} item - * @param {boolean} forceRead read content even if it is unattributed and deleted + * @param {Array>} contents - where to write the result + * @param {number} client + * @param {number} clock + * @param {boolean} deleted + * @param {AbstractContent} content + * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation */ - readContent (contents, item, forceRead) { - const deleted = item.deleted - const slice = (deleted ? this.deletes : this.inserts).sliceId(item.id, item.length) - let content = slice.length === 1 ? item.content : item.content.copy() + readContent (contents, client, clock, deleted, content, shouldRender) { + const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, content.getLength()) + content = slice.length === 1 ? content : content.copy() slice.forEach(s => { const c = content if (s.len < c.getLength()) { content = c.splice(s.len) } - if (!deleted || s.attrs != null || forceRead) { - contents.push(new AttributedContent(c, deleted, s.attrs)) + if (!deleted || s.attrs != null) { + contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) } }) } @@ -147,13 +156,16 @@ export class NoAttributionsManager { destroy () {} /** - * @param {Array>} contents - * @param {Item} item - * @param {boolean} forceRead read content even if it is unattributed and deleted + * @param {Array>} contents - where to write the result + * @param {number} _client + * @param {number} _clock + * @param {boolean} deleted + * @param {AbstractContent} content + * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation */ - readContent (contents, item, forceRead) { - if (!item.deleted || forceRead) { - contents.push(new AttributedContent(item.content, false, null)) + readContent (contents, _client, _clock, deleted, content, shouldRender) { + if (!deleted || shouldRender) { + contents.push(new AttributedContent(content, deleted, null, shouldRender)) } } } @@ -219,21 +231,23 @@ export class DiffAttributionManager { } /** - * @param {Array>} contents - * @param {Item} item - * @param {boolean} forceRead read content even if it is unattributed and deleted + * @param {Array>} contents - where to write the result + * @param {number} client + * @param {number} clock + * @param {boolean} deleted + * @param {AbstractContent} content + * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation */ - readContent (contents, item, forceRead) { - const deleted = item.deleted || /** @type {any} */ (item.parent).doc !== this._nextDoc - const slice = (deleted ? this.deletes : this.inserts).sliceId(item.id, item.length) - let content = slice.length === 1 ? item.content : item.content.copy() - if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.hasId(item.id)) { + readContent (contents, client, clock, deleted, content, shouldRender) { + const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, content.getLength()) + content = slice.length === 1 ? content : content.copy() + if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.has(client, clock)) { // Retrieved item is never more fragmented than the newer item. - const prevItem = getItem(this._prevDocStore, item.id) + const prevItem = getItem(this._prevDocStore, createID(client, clock)) content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content // trim itemContent to the correct size. - const diffStart = prevItem.id.clock - item.id.clock - const diffEnd = prevItem.id.clock + prevItem.length - item.id.clock - item.length + const diffStart = prevItem.id.clock - clock + const diffEnd = prevItem.id.clock + prevItem.length - clock - content.getLength() if (diffStart > 0) { content = content.splice(diffStart) } @@ -246,8 +260,8 @@ export class DiffAttributionManager { if (s.len < c.getLength()) { content = c.splice(s.len) } - if (!deleted || s.attrs != null || forceRead) { - contents.push(new AttributedContent(c, deleted, s.attrs)) + if (!deleted || s.attrs != null || shouldRender) { + contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) } }) } @@ -288,28 +302,31 @@ export class SnapshotAttributionManager { destroy () { } /** - * @param {Array>} contents - * @param {Item} item - * @param {boolean} forceRead read content even if it is unattributed and deleted + * @param {Array>} contents - where to write the result + * @param {number} client + * @param {number} clock + * @param {boolean} _deleted + * @param {AbstractContent} content + * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation */ - readContent (contents, item, forceRead) { - if ((this.nextSnapshot.sv.get(item.id.client) ?? 0) <= item.id.clock) return // future item that should not be displayed - const slice = this.attrs.sliceId(item.id, item.length) - let content = slice.length === 1 ? item.content : item.content.copy() + readContent (contents, client, clock, _deleted, content, shouldRender) { + if ((this.nextSnapshot.sv.get(client) ?? 0) <= clock) return // future item that should not be displayed + const slice = this.attrs.slice(client, clock, content.getLength()) + content = slice.length === 1 ? content : content.copy() slice.forEach(s => { - const deleted = this.nextSnapshot.ds.has(item.id.client, s.clock) - const nonExistend = (this.nextSnapshot.sv.get(item.id.client) ?? 0) <= s.clock + const deleted = this.nextSnapshot.ds.has(client, s.clock) + const nonExistend = (this.nextSnapshot.sv.get(client) ?? 0) <= s.clock const c = content if (s.len < c.getLength()) { content = c.splice(s.len) } if (nonExistend) return - if (!deleted || forceRead || (s.attrs != null && s.attrs.length > 0)) { + if (!deleted || shouldRender || (s.attrs != null && s.attrs.length > 0)) { let attrsWithoutChange = s.attrs?.filter(attr => attr.name !== 'change') ?? null if (s.attrs?.length === 0) { attrsWithoutChange = null } - contents.push(new AttributedContent(c, deleted, attrsWithoutChange)) + contents.push(new AttributedContent(c, deleted, attrsWithoutChange, shouldRender)) } }) } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index b06072791..27f5fca2e 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2267,7 +2267,7 @@ export const testAttributedContent = _tc => { }) t.group('unformat', () => { ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) - const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') + const expectedContent = delta.createTextDelta().insert('Hell', { italic: null }, { attributes: { italic: [] } }).insert('o attributions!') const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index a0c0611ca..44a4eb845 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -315,15 +315,16 @@ export const testElementAttributedContentViaDiffer = _tc => { yelement.setAttribute('key', '42') }) const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) - const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello', null, { delete: [] })], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) + const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', attributedContent.children.toJSON()) console.log('attributes', attributedContent.attributes) + t.compare(attributedContent.children.toJSON(), expectedContent.toJSON()) t.assert(attributedContent.children.equals(expectedContent)) t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { const expectedContent = delta.createArrayDelta().insert( - [delta.createTextDelta().insert('hello', null, { delete: [] })], + [delta.createTextDelta().insert('hello')], null, { delete: [] } ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) @@ -344,7 +345,7 @@ export const testElementAttributedContentViaDiffer = _tc => { t.group('test getContentDeep after some more updates', () => { t.info('expecting diffingAttributionManager to auto update itself') const expectedContent = delta.createArrayDelta().insert( - [delta.createTextDelta().insert('hello', null, { delete: [] })], + [delta.createTextDelta().insert('hello')], null, { delete: [] } ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) From 7d5d6b840fe4c8952e425e78a4ffa3debad7d402 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 19 May 2025 00:50:34 +0200 Subject: [PATCH 312/362] fix some edge cases --- src/types/AbstractType.js | 10 +++++----- src/types/YText.js | 21 +++++++++++++++++---- src/utils/AttributionManager.js | 2 +- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index c1856d345..621190356 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -514,15 +514,15 @@ export const typeListGetContent = (type, am) => { for (let item = type._start; item !== null; cs.length = 0) { // populate cs for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, true) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted) } for (let i = 0; i < cs.length; i++) { const c = cs[i] const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted).attribution if (c.content.isCountable()) { - if (c.render) { + if (c.render || attribution != null) { d.insert(c.content.getContent(), null, attribution) - } else { + } else if (!c.deleted) { d.retain(c.content.getLength()) } } @@ -1011,7 +1011,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const cs = [] - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, true) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) const { attribution } = createAttributionFromAttributionItems(attrs, deleted) @@ -1027,7 +1027,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const tmpcs = [] - am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, true) + am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, !prevItem.deleted) cs = tmpcs.concat(cs) if (cs.length === 0 || cs[0].attrs == null) { cs.splice(0, cs.findIndex(c => c.attrs != null)) diff --git a/src/types/YText.js b/src/types/YText.js index 16c37e2a0..8e46f7466 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -25,7 +25,10 @@ import { ContentType, warnPrematureAccess, noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line - createAttributionFromAttributionItems + createAttributionFromAttributionItems, + mergeIdSets, + diffIdSet, + intersectSets } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -775,6 +778,16 @@ export class YTextEvent extends YEvent { } }) return d.done() + // const whatToWatch = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) + // const genericDelta = this.target.getDelta(am, whatToWatch) + // if (!d.equals(genericDelta)) { + // console.log(d.toJSON()) + // console.log(genericDelta.toJSON()) + // debugger + // const d2 = this.target.getDelta(am, whatToWatch) + // throw new Error('should match', d2) + // } + // return d } /** @@ -1117,12 +1130,12 @@ export class YText extends AbstractType { } } else { for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, true) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted) } } for (let i = 0; i < cs.length; i++) { const c = cs[i] - const renderDelete = c.deleted && c.attrs != null && c.render + const renderDelete = c.deleted && (c.attrs != null || c.render) const renderInsert = !c.deleted && (c.render || c.attrs != null) const attribution = (renderDelete || renderInsert) ? createAttributionFromAttributionItems(c.attrs, c.deleted).attribution : null switch (c.content.constructor) { @@ -1179,7 +1192,7 @@ export class YText extends AbstractType { changedAttributes[key] = value } } else if (renderDelete) { - if (equalAttrs(value,currAttrVal)) { + if (equalAttrs(value, currAttrVal)) { delete changedAttributes[key] delete currentAttributes[key] } else { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index b266f26de..d80784853 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -326,7 +326,7 @@ export class SnapshotAttributionManager { if (s.attrs?.length === 0) { attrsWithoutChange = null } - contents.push(new AttributedContent(c, deleted, attrsWithoutChange, shouldRender)) + contents.push(new AttributedContent(c, deleted, attrsWithoutChange, !deleted)) } }) } From e1ef2210d9bca87aa40e82a9fac1ad14a4b57f66 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 19 May 2025 17:52:59 +0200 Subject: [PATCH 313/362] only have a single getDelta implementation for events and retrieving content --- package.json | 6 +- src/types/YText.js | 220 ++++++++++++++++++++++++++---------------- tests/y-text.tests.js | 4 +- 3 files changed, 140 insertions(+), 90 deletions(-) diff --git a/package.json b/package.json index f0d4a1905..f099f1bdf 100644 --- a/package.json +++ b/package.json @@ -29,19 +29,19 @@ "exports": { ".": { "types": "./dist/src/index.d.ts", - "module": "./dist/yjs.mjs", + "module": "./src/index.js", "require": "./dist/yjs.cjs", "import": "./src/index.js" }, "./internals": { "types": "./dist/src/internals.d.ts", - "module": "./dist/internals.mjs", + "module": "./src/internals.js", "require": "./dist/internals.cjs", "import": "./src/internals.js" }, "./testHelper": { "types": "./dist/testHelper.d.ts", - "module": "./dist/testHelper.mjs", + "module": "./tests/testHelper.js", "require": "./dist/testHelper.cjs", "import": "./tests/testHelper.js" }, diff --git a/src/types/YText.js b/src/types/YText.js index 8e46f7466..aa3da2c5c 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -33,6 +33,7 @@ import { import * as delta from '../utils/Delta.js' +import * as traits from 'lib0/traits' import * as object from 'lib0/object' import * as map from 'lib0/map' import * as error from 'lib0/error' @@ -649,6 +650,18 @@ export class YTextEvent extends YEvent { * @public */ getDelta (am = noAttributionsManager) { + const whatToWatch = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) + const genericDelta = this.target.getDelta(am, whatToWatch) + return genericDelta; + /* + if (!d.equals(genericDelta)) { + console.log(d.toJSON()) + console.log(genericDelta.toJSON()) + debugger + const d2 = this.target.getDelta(am, whatToWatch) + throw new Error('should match', d2) + } + return d const ydoc = /** @type {Doc} */ (this.target.doc) /** * @type {import('../utils/Delta.js').TextDelta} @@ -729,23 +742,42 @@ export class YTextEvent extends YEvent { if (equalAttrs(value, currAttrVal)) { item.delete(transaction) } else if (equalAttrs(value, previousAttributes[key] ?? null)) { - delete currentAttributes[key] delete changedAttributes[key] } else { - currentAttributes[key] = value changedAttributes[key] = value } + if (value == null) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } } else if (freshDelete) { - changedAttributes[key] = currAttrVal - currentAttributes[key] = currAttrVal + if (equalAttrs(value, currAttrVal)) { + // nop + } else if (equalAttrs(currAttrVal, previousAttributes[key] ?? null)) { + delete changedAttributes[key] + } else { + changedAttributes[key] = currAttrVal + } + // current attributes doesn't change previousAttributes[key] = value + } else if (!c.deleted) { // fresh reference to currentAttributes only if (usingCurrentAttributes) { currentAttributes = object.assign({}, currentAttributes) usingCurrentAttributes = false } - currentAttributes[key] = value + if (usingChangedAttributes && changedAttributes[key] !== undefined) { + usingChangedAttributes = false + changedAttributes = object.assign({}, changedAttributes) + } + if (value == null) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } + delete changedAttributes[key] previousAttributes[key] = value } // # Update Attributions @@ -788,6 +820,7 @@ export class YTextEvent extends YEvent { // throw new Error('should match', d2) // } // return d + // */ } /** @@ -1005,81 +1038,81 @@ export class YText extends AbstractType { */ getContent (am = noAttributionsManager) { return this.getDelta(am) - this.doc ?? warnPrematureAccess() - /** - * @type {delta.TextDelta} - */ - const d = delta.createTextDelta() - /** - * @type {Array>} - */ - const cs = [] - for (let item = this._start; item !== null; cs.length = 0) { - // populate cs - for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item, false) - } - for (let i = 0; i < cs.length; i++) { - const { content, deleted, attrs } = cs[i] - const { attribution, retainOnly } = createAttributionFromAttributionItems(attrs, deleted) - switch (content.constructor) { - case ContentString: { - if (retainOnly) { - d.retain(content.getLength(), null, attribution) - } else { - d.insert(/** @type {ContentString} */ (content).str, null, attribution) - } - break - } - case ContentType: - case ContentEmbed: { - if (retainOnly) { - d.retain(content.getLength(), null, attribution) - } else { - d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution) - } - break - } - case ContentFormat: { - const contentFormat = /** @type {ContentFormat} */ (content) - if (attribution != null) { - /** - * @type {import('../utils/Delta.js').Attribution} - */ - const formattingAttribution = object.assign({}, d.usedAttribution) - const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - if (contentFormat.value === null) { - delete attributesChanged[contentFormat.key] - } else { - const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] - by.push(...((deleted ? attribution.delete : attribution.insert) ?? [])) - const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt) formattingAttribution.attributedAt = attributedAt - } - if (object.isEmpty(attributesChanged)) { - d.useAttribution(null) - } else { - const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt != null) formattingAttribution.attributedAt = attributedAt - d.useAttribution(formattingAttribution) - } - } - if (!deleted) { - const currAttrs = d.usedAttributes - if (contentFormat.value == null) { - const nextAttrs = object.assign({}, currAttrs) - delete nextAttrs[contentFormat.key] - d.useAttributes(nextAttrs) - } else { - d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) - } - } - break - } - } - } - } - return d + // this.doc ?? warnPrematureAccess() + // /** + // * @type {delta.TextDelta} + // */ + // const d = delta.createTextDelta() + // /** + // * @type {Array>} + // */ + // const cs = [] + // for (let item = this._start; item !== null; cs.length = 0) { + // // populate cs + // for (; item !== null && cs.length < 50; item = item.right) { + // am.readContent(cs, item, false) + // } + // for (let i = 0; i < cs.length; i++) { + // const { content, deleted, attrs } = cs[i] + // const { attribution, retainOnly } = createAttributionFromAttributionItems(attrs, deleted) + // switch (content.constructor) { + // case ContentString: { + // if (retainOnly) { + // d.retain(content.getLength(), null, attribution) + // } else { + // d.insert(/** @type {ContentString} */ (content).str, null, attribution) + // } + // break + // } + // case ContentType: + // case ContentEmbed: { + // if (retainOnly) { + // d.retain(content.getLength(), null, attribution) + // } else { + // d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution) + // } + // break + // } + // case ContentFormat: { + // const contentFormat = /** @type {ContentFormat} */ (content) + // if (attribution != null) { + // /** + // * @type {import('../utils/Delta.js').Attribution} + // */ + // const formattingAttribution = object.assign({}, d.usedAttribution) + // const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + // if (contentFormat.value === null) { + // delete attributesChanged[contentFormat.key] + // } else { + // const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] + // by.push(...((deleted ? attribution.delete : attribution.insert) ?? [])) + // const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) + // if (attributedAt) formattingAttribution.attributedAt = attributedAt + // } + // if (object.isEmpty(attributesChanged)) { + // d.useAttribution(null) + // } else { + // const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) + // if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + // d.useAttribution(formattingAttribution) + // } + // } + // if (!deleted) { + // const currAttrs = d.usedAttributes + // if (contentFormat.value == null) { + // const nextAttrs = object.assign({}, currAttrs) + // delete nextAttrs[contentFormat.key] + // d.useAttributes(nextAttrs) + // } else { + // d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) + // } + // } + // break + // } + // } + // } + // } + // return d } /** @@ -1169,6 +1202,7 @@ export class YText extends AbstractType { case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (c.content) const currAttrVal = currentAttributes[key] ?? null + // @todo write a function "updateCurrentAttributes" and "updateChangedAttributes" // # Update Attributes if (renderDelete || renderInsert) { // create fresh references @@ -1185,20 +1219,24 @@ export class YText extends AbstractType { if (equalAttrs(value, currAttrVal)) { // item.delete(transaction) } else if (equalAttrs(value, previousAttributes[key] ?? null)) { - delete currentAttributes[key] delete changedAttributes[key] } else { - currentAttributes[key] = value changedAttributes[key] = value } + if (value == null) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } } else if (renderDelete) { if (equalAttrs(value, currAttrVal)) { + // nop + } else if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { delete changedAttributes[key] - delete currentAttributes[key] } else { changedAttributes[key] = currAttrVal - currentAttributes[key] = currAttrVal } + // current attributes doesn't change previousAttributes[key] = value } else if (!c.deleted) { // fresh reference to currentAttributes only @@ -1206,11 +1244,16 @@ export class YText extends AbstractType { currentAttributes = object.assign({}, currentAttributes) usingCurrentAttributes = false } - if (equalAttrs(value, previousAttributes[key] ?? null)) { + if (usingChangedAttributes && changedAttributes[key] !== undefined) { + usingChangedAttributes = false + changedAttributes = object.assign({}, changedAttributes) + } + if (value == null) { delete currentAttributes[key] } else { currentAttributes[key] = value } + delete changedAttributes[key] previousAttributes[key] = value } // # Update Attributions @@ -1419,6 +1462,13 @@ export class YText extends AbstractType { _write (encoder) { encoder.writeTypeRef(YTextRefID) } + + /** + * @param {this} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.getContent().equals(other.getContent()) + } } /** diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 27f5fca2e..f3587e3d5 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1621,7 +1621,7 @@ export const testDeltaAfterConcurrentFormatting = tc => { } }) testConnector.flushAllMessages() - t.compare(deltas, [[{ retain: 3, attributes: { bold: true } }, { retain: 2, attributes: { bold: null } }]]) + t.compare(deltas, [[{ retain: 2, attributes: { bold: true } }, { retain: 1 }, { retain: 1, attributes: { bold: null } }]]) } /** @@ -2267,7 +2267,7 @@ export const testAttributedContent = _tc => { }) t.group('unformat', () => { ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) - const expectedContent = delta.createTextDelta().insert('Hell', { italic: null }, { attributes: { italic: [] } }).insert('o attributions!') + const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) From 3fd60a2017b9b7821d91673a9a6c90546e016c25 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 21 May 2025 16:04:55 +0200 Subject: [PATCH 314/362] first tests on attributed events --- src/index.js | 3 +- src/types/YText.js | 75 ++++++++++++++++++--------------- src/utils/AttributionManager.js | 29 +++++++------ src/utils/Doc.js | 13 +++++- tests/attribution.tests.js | 34 +++++++++++++++ tests/index.js | 3 +- 6 files changed, 107 insertions(+), 50 deletions(-) create mode 100644 tests/attribution.tests.js diff --git a/src/index.js b/src/index.js index 1aebeb814..94a280128 100644 --- a/src/index.js +++ b/src/index.js @@ -115,7 +115,8 @@ export { noAttributionsManager, iterateStructsByIdSet, createAttributionManagerFromDiff, - createIdSet + createIdSet, + cloneDoc } from './internals.js' const glo = /** @type {any} */ (typeof globalThis !== 'undefined' diff --git a/src/types/YText.js b/src/types/YText.js index aa3da2c5c..0e01daf30 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1158,42 +1158,47 @@ export class YText extends AbstractType { if (ir !== rslice.length - 1) { itemContent.splice(idrange.len) } - am.readContent(cs, item.id.client, idrange.clock, item.deleted, content, idrange.exists) + am.readContent(cs, item.id.client, idrange.clock, item.deleted, content, idrange.exists ? 2 : 0) } } } else { for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) } } for (let i = 0; i < cs.length; i++) { const c = cs[i] - const renderDelete = c.deleted && (c.attrs != null || c.render) - const renderInsert = !c.deleted && (c.render || c.attrs != null) - const attribution = (renderDelete || renderInsert) ? createAttributionFromAttributionItems(c.attrs, c.deleted).attribution : null + // render (attributed) content even if it was deleted + const renderContent = c.render && (!c.deleted || c.attrs != null) + // content that was just deleted. It is not rendered as an insertion, because it doesn't + // have any attributes. + const renderDelete = c.render && c.deleted + // existing content that should be retained, only adding changed attributes + const retainContent = !c.render && (!c.deleted || c.attrs != null) + const attribution = renderContent ? createAttributionFromAttributionItems(c.attrs, c.deleted).attribution : null switch (c.content.constructor) { case ContentType: case ContentEmbed: - if (renderInsert) { + if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true d.insert(c.content.getContent()[0], null, attribution) } else if (renderDelete) { d.delete(1) - } else if (!c.deleted) { + } else if (retainContent) { d.usedAttributes = changedAttributes usingChangedAttributes = true d.retain(1) } break case ContentString: - if (renderInsert || (renderDelete && attribution?.delete != null)) { + if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) } else if (renderDelete) { d.delete(c.content.getLength()) - } else if (!c.deleted) { + } else if (retainContent) { d.usedAttributes = changedAttributes usingChangedAttributes = true d.retain(c.content.getLength()) @@ -1204,7 +1209,7 @@ export class YText extends AbstractType { const currAttrVal = currentAttributes[key] ?? null // @todo write a function "updateCurrentAttributes" and "updateChangedAttributes" // # Update Attributes - if (renderDelete || renderInsert) { + if (renderContent || renderDelete) { // create fresh references if (usingCurrentAttributes) { currentAttributes = object.assign({}, currentAttributes) @@ -1215,30 +1220,34 @@ export class YText extends AbstractType { changedAttributes = object.assign({}, changedAttributes) } } - if (renderInsert) { - if (equalAttrs(value, currAttrVal)) { - // item.delete(transaction) - } else if (equalAttrs(value, previousAttributes[key] ?? null)) { - delete changedAttributes[key] - } else { - changedAttributes[key] = value - } - if (value == null) { - delete currentAttributes[key] - } else { - currentAttributes[key] = value - } - } else if (renderDelete) { - if (equalAttrs(value, currAttrVal)) { - // nop - } else if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { - delete changedAttributes[key] - } else { - changedAttributes[key] = currAttrVal + if (renderContent || renderDelete) { + if (c.deleted) { + // content was deleted, but is possibly attributed + if (equalAttrs(value, currAttrVal)) { + // nop + } else if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { + delete changedAttributes[key] + } else { + changedAttributes[key] = currAttrVal + } + // current attributes doesn't change + previousAttributes[key] = value + } else { // !c.deleted + // content was inserted, and is possibly attributed + if (equalAttrs(value, currAttrVal)) { + // item.delete(transaction) + } else if (equalAttrs(value, previousAttributes[key] ?? null)) { + delete changedAttributes[key] + } else { + changedAttributes[key] = value + } + if (value == null) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } } - // current attributes doesn't change - previousAttributes[key] = value - } else if (!c.deleted) { + } else if (retainContent && !c.deleted) { // fresh reference to currentAttributes only if (usingCurrentAttributes) { currentAttributes = object.assign({}, currentAttributes) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index d80784853..95c6abaea 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -80,13 +80,13 @@ export class AttributedContent { * @param {AbstractContent} content * @param {boolean} deleted * @param {Array> | null} attrs - * @param {boolean} render + * @param {0|1|2} renderBehavior */ - constructor (content, deleted, attrs, render) { + constructor (content, deleted, attrs, renderBehavior) { this.content = content this.deleted = deleted this.attrs = attrs - this.render = render + this.render = renderBehavior === 0 ? false : (renderBehavior === 1 ? (!deleted || attrs != null) : true) } } @@ -100,7 +100,7 @@ export class AbstractAttributionManager { * @param {number} _clock * @param {boolean} _deleted * @param {AbstractContent} _content - * @param {boolean} _shouldRender - whether this should render or just result in a `retain` operation + * @param {0|1|2} _shouldRender - 0: if undeleted or attributed, render as a retain operation. 1: render only if undeleted or attributed. 2: render as insert operation (if unattributed and deleted, render as delete). */ readContent (_contents, _client, _clock, _deleted, _content, _shouldRender) { error.methodUnimplemented() @@ -130,7 +130,7 @@ export class TwosetAttributionManager { * @param {number} clock * @param {boolean} deleted * @param {AbstractContent} content - * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation + * @param {0|1|2} shouldRender - whether this should render or just result in a `retain` operation */ readContent (contents, client, clock, deleted, content, shouldRender) { const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, content.getLength()) @@ -140,7 +140,7 @@ export class TwosetAttributionManager { if (s.len < c.getLength()) { content = c.splice(s.len) } - if (!deleted || s.attrs != null) { + if (!deleted || s.attrs != null || shouldRender) { contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) } }) @@ -161,7 +161,7 @@ export class NoAttributionsManager { * @param {number} _clock * @param {boolean} deleted * @param {AbstractContent} content - * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation + * @param {0|1|2} shouldRender - whether this should render or just result in a `retain` operation */ readContent (contents, _client, _clock, deleted, content, shouldRender) { if (!deleted || shouldRender) { @@ -236,7 +236,7 @@ export class DiffAttributionManager { * @param {number} clock * @param {boolean} deleted * @param {AbstractContent} content - * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation + * @param {0|1|2} shouldRender - whether this should render or just result in a `retain` operation */ readContent (contents, client, clock, deleted, content, shouldRender) { const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, content.getLength()) @@ -244,10 +244,11 @@ export class DiffAttributionManager { if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.has(client, clock)) { // Retrieved item is never more fragmented than the newer item. const prevItem = getItem(this._prevDocStore, createID(client, clock)) + const originalContentLen = content.getLength() content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content // trim itemContent to the correct size. - const diffStart = prevItem.id.clock - clock - const diffEnd = prevItem.id.clock + prevItem.length - clock - content.getLength() + const diffStart = clock - prevItem.id.clock + const diffEnd = prevItem.id.clock + prevItem.length - clock - originalContentLen if (diffStart > 0) { content = content.splice(diffStart) } @@ -260,7 +261,7 @@ export class DiffAttributionManager { if (s.len < c.getLength()) { content = c.splice(s.len) } - if (!deleted || s.attrs != null || shouldRender) { + if (shouldRender || !deleted || s.attrs != null) { contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) } }) @@ -307,7 +308,7 @@ export class SnapshotAttributionManager { * @param {number} clock * @param {boolean} _deleted * @param {AbstractContent} content - * @param {boolean} shouldRender - whether this should render or just result in a `retain` operation + * @param {0|1|2} shouldRender - whether this should render or just result in a `retain` operation */ readContent (contents, client, clock, _deleted, content, shouldRender) { if ((this.nextSnapshot.sv.get(client) ?? 0) <= clock) return // future item that should not be displayed @@ -321,12 +322,12 @@ export class SnapshotAttributionManager { content = c.splice(s.len) } if (nonExistend) return - if (!deleted || shouldRender || (s.attrs != null && s.attrs.length > 0)) { + if (shouldRender || !deleted || (s.attrs != null && s.attrs.length > 0)) { let attrsWithoutChange = s.attrs?.filter(attr => attr.name !== 'change') ?? null if (s.attrs?.length === 0) { attrsWithoutChange = null } - contents.push(new AttributedContent(c, deleted, attrsWithoutChange, !deleted)) + contents.push(new AttributedContent(c, deleted, attrsWithoutChange, shouldRender)) } }) } diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 317f8c213..83c65f3a7 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -11,7 +11,9 @@ import { YXmlElement, YXmlFragment, transact, - ContentDoc, Item, Transaction, YEvent // eslint-disable-line + applyUpdate, + ContentDoc, Item, Transaction, YEvent, // eslint-disable-line + encodeStateAsUpdate } from '../internals.js' import { ObservableV2 } from 'lib0/observable' @@ -345,3 +347,12 @@ export class Doc extends ObservableV2 { super.destroy() } } + +/** + * @param {Doc} ydoc + */ +export const cloneDoc = ydoc => { + const clone = new Doc() + applyUpdate(clone, encodeStateAsUpdate(ydoc)) + return clone +} diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js new file mode 100644 index 000000000..c4707a7c8 --- /dev/null +++ b/tests/attribution.tests.js @@ -0,0 +1,34 @@ +/** + * Testing if encoding/decoding compatibility and integration compatibility is given. + * We expect that the document always looks the same, even if we upgrade the integration algorithm, or add additional encoding approaches. + * + * The v1 documents were generated with Yjs v13.2.0 based on the randomisized tests. + */ + +import * as Y from '../src/index.js' +import * as t from 'lib0/testing' +import * as delta from '../src/utils/Delta.js' + +/** + * @param {t.TestCase} _tc + */ +export const testAttributedEvents = _tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + ytext.insert(0, 'hello world') + const v1 = Y.cloneDoc(ydoc) + ydoc.transact(() => { + ytext.delete(6, 5) + }) + let am = Y.createAttributionManagerFromDiff(v1, ydoc) + const c1 = ytext.getDelta(am) + t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) + let calledObserver = false + ytext.observe(event => { + const d = event.getDelta(am) + t.compare(d, delta.createTextDelta().retain(11).insert('!', null, { insert: [] })) + calledObserver = true + }) + ytext.insert(11, '!') + t.assert(calledObserver) +} diff --git a/tests/index.js b/tests/index.js index 753174da4..148b524bb 100644 --- a/tests/index.js +++ b/tests/index.js @@ -14,6 +14,7 @@ import * as relativePositions from './relativePositions.tests.js' import * as delta from './delta.tests.js' import * as idset from './IdSet.tests.js' import * as idmap from './IdMap.tests.js' +import * as attribution from './attribution.tests.js' import { runTests } from 'lib0/testing' import { isBrowser, isNode } from 'lib0/environment' @@ -24,7 +25,7 @@ if (isBrowser) { } const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset, idmap + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset, idmap, attribution } const run = async () => { From 5b29e54a59c25df3c3edc2255fdf935f25c3c193 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 21 May 2025 22:11:28 +0200 Subject: [PATCH 315/362] be able to insert into attributed content --- src/types/YText.js | 369 +++++++++----------------------- src/utils/AttributionManager.js | 55 ++++- tests/attribution.tests.js | 36 ++++ 3 files changed, 188 insertions(+), 272 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 0e01daf30..5ef624190 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -27,8 +27,7 @@ import { noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line createAttributionFromAttributionItems, mergeIdSets, - diffIdSet, - intersectSets + diffIdSet } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -51,12 +50,14 @@ export class ItemTextListPosition { * @param {Item|null} right * @param {number} index * @param {Map} currentAttributes + * @param {AbstractAttributionManager} am */ - constructor (left, right, index, currentAttributes) { + constructor (left, right, index, currentAttributes, am) { this.left = left this.right = right this.index = index this.currentAttributes = currentAttributes + this.am = am } /** @@ -73,14 +74,86 @@ export class ItemTextListPosition { } break default: - if (!this.right.deleted) { - this.index += this.right.length - } + this.index += this.am.contentLength(this.right) break } this.left = this.right this.right = this.right.right } + + /** + * @param {Transaction} transaction + * @param {AbstractType} parent + * @param {number} length + * @param {Object} attributes + * + * @function + */ + formatText (transaction, parent, length, attributes) { + const doc = transaction.doc + const ownClientId = doc.clientID + minimizeAttributeChanges(this, attributes) + const negatedAttributes = insertAttributes(transaction, parent, this, attributes) + // iterate until first non-format or null is found + // delete all formats with attributes[format.key] != null + // also check the attributes after the first non-format as we do not want to insert redundant negated attributes there + // eslint-disable-next-line no-labels + iterationLoop: while ( + this.right !== null && + (length > 0 || + ( + negatedAttributes.size > 0 && + (this.right.deleted || this.right.content.constructor === ContentFormat) + ) + ) + ) { + switch (this.right.content.constructor) { + case ContentFormat: { + if (!this.right.deleted) { + const { key, value } = /** @type {ContentFormat} */ (this.right.content) + const attr = attributes[key] + if (attr !== undefined) { + if (equalAttrs(attr, value)) { + negatedAttributes.delete(key) + } else { + if (length === 0) { + // no need to further extend negatedAttributes + // eslint-disable-next-line no-labels + break iterationLoop + } + negatedAttributes.set(key, value) + } + this.right.delete(transaction) + } else { + this.currentAttributes.set(key, value) + } + } + break + } + default: + const rightLen = this.am.contentLength(this.right) + if (length < rightLen) { + getItemCleanStart(transaction, createID(this.right.id.client, this.right.id.clock + length)) + } + length -= rightLen + break + } + this.forward() + } + // Quill just assumes that the editor starts with a newline and that it always + // ends with a newline. We only insert that newline when a new newline is + // inserted - i.e when length is bigger than type.length + if (length > 0) { + let newlines = '' + for (; length > 0; length--) { + newlines += '\n' + } + this.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), this.left, this.left && this.left.lastId, this.right, this.right && this.right.id, parent, null, new ContentString(newlines)) + this.right.integrate(transaction, 0) + this.forward() + } + insertNegatedAttributes(transaction, parent, this, negatedAttributes) + } } /** @@ -132,10 +205,10 @@ const findPosition = (transaction, parent, index, useSearchMarker) => { const currentAttributes = new Map() const marker = useSearchMarker ? findMarker(parent, index) : null if (marker) { - const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes) + const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes, noAttributionsManager) return findNextPosition(transaction, pos, index - marker.index) } else { - const pos = new ItemTextListPosition(null, parent._start, 0, currentAttributes) + const pos = new ItemTextListPosition(null, parent._start, 0, currentAttributes, noAttributionsManager) return findNextPosition(transaction, pos, index) } } @@ -279,81 +352,6 @@ const insertText = (transaction, parent, currPos, text, attributes) => { insertNegatedAttributes(transaction, parent, currPos, negatedAttributes) } -/** - * @param {Transaction} transaction - * @param {AbstractType} parent - * @param {ItemTextListPosition} currPos - * @param {number} length - * @param {Object} attributes - * - * @private - * @function - */ -const formatText = (transaction, parent, currPos, length, attributes) => { - const doc = transaction.doc - const ownClientId = doc.clientID - minimizeAttributeChanges(currPos, attributes) - const negatedAttributes = insertAttributes(transaction, parent, currPos, attributes) - // iterate until first non-format or null is found - // delete all formats with attributes[format.key] != null - // also check the attributes after the first non-format as we do not want to insert redundant negated attributes there - // eslint-disable-next-line no-labels - iterationLoop: while ( - currPos.right !== null && - (length > 0 || - ( - negatedAttributes.size > 0 && - (currPos.right.deleted || currPos.right.content.constructor === ContentFormat) - ) - ) - ) { - if (!currPos.right.deleted) { - switch (currPos.right.content.constructor) { - case ContentFormat: { - const { key, value } = /** @type {ContentFormat} */ (currPos.right.content) - const attr = attributes[key] - if (attr !== undefined) { - if (equalAttrs(attr, value)) { - negatedAttributes.delete(key) - } else { - if (length === 0) { - // no need to further extend negatedAttributes - // eslint-disable-next-line no-labels - break iterationLoop - } - negatedAttributes.set(key, value) - } - currPos.right.delete(transaction) - } else { - currPos.currentAttributes.set(key, value) - } - break - } - default: - if (length < currPos.right.length) { - getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length)) - } - length -= currPos.right.length - break - } - } - currPos.forward() - } - // Quill just assumes that the editor starts with a newline and that it always - // ends with a newline. We only insert that newline when a new newline is - // inserted - i.e when length is bigger than type.length - if (length > 0) { - let newlines = '' - for (; length > 0; length--) { - newlines += '\n' - } - currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), currPos.left, currPos.left && currPos.left.lastId, currPos.right, currPos.right && currPos.right.id, parent, null, new ContentString(newlines)) - currPos.right.integrate(transaction, 0) - currPos.forward() - } - insertNegatedAttributes(transaction, parent, currPos, negatedAttributes) -} - /** * Call this function after string content has been deleted in order to * clean up formatting Items. @@ -651,176 +649,7 @@ export class YTextEvent extends YEvent { */ getDelta (am = noAttributionsManager) { const whatToWatch = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - const genericDelta = this.target.getDelta(am, whatToWatch) - return genericDelta; - /* - if (!d.equals(genericDelta)) { - console.log(d.toJSON()) - console.log(genericDelta.toJSON()) - debugger - const d2 = this.target.getDelta(am, whatToWatch) - throw new Error('should match', d2) - } - return d - const ydoc = /** @type {Doc} */ (this.target.doc) - /** - * @type {import('../utils/Delta.js').TextDelta} - */ - const d = delta.createTextDelta() - transact(ydoc, transaction => { - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - let currentAttributes = {} // saves all current attributes for insert - let usingCurrentAttributes = false - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - let changedAttributes = {} // saves changed attributes for retain - let usingChangedAttributes = false - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - const previousAttributes = {} // The value before changes - const tr = this.transaction - - /** - * @type {Array>} - */ - const cs = [] - for (let item = this.target._start; item !== null; cs.length = 0, item = item.right) { - const freshDelete = item.deleted && tr.deleteSet.hasId(item.id) && !tr.insertSet.hasId(item.id) - const freshInsert = !item.deleted && tr.insertSet.hasId(item.id) - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted || freshDelete) // do item.right after calling this - for (let i = 0; i < cs.length; i++) { - const c = cs[i] - const { attribution } = createAttributionFromAttributionItems(c.attrs, c.deleted) - switch (c.content.constructor) { - case ContentType: - case ContentEmbed: - if (freshInsert) { - d.usedAttributes = currentAttributes - usingCurrentAttributes = true - d.insert(c.content.getContent()[0], null, attribution) - } else if (freshDelete) { - d.delete(1) - } else if (!c.deleted) { - d.usedAttributes = changedAttributes - usingChangedAttributes = true - d.retain(1) - } - break - case ContentString: - if (freshInsert) { - d.usedAttributes = currentAttributes - usingCurrentAttributes = true - d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) - } else if (freshDelete) { - d.delete(c.content.getLength()) - } else if (!c.deleted) { - d.usedAttributes = changedAttributes - usingChangedAttributes = true - d.retain(c.content.getLength()) - } - break - case ContentFormat: { - const { key, value } = /** @type {ContentFormat} */ (c.content) - // # update attributes - const currAttrVal = currentAttributes[key] ?? null - if (freshDelete || freshInsert) { - // create fresh references - if (usingCurrentAttributes) { - currentAttributes = object.assign({}, currentAttributes) - usingCurrentAttributes = false - } - if (usingChangedAttributes) { - usingChangedAttributes = false - changedAttributes = object.assign({}, changedAttributes) - } - } - if (freshInsert) { - if (equalAttrs(value, currAttrVal)) { - item.delete(transaction) - } else if (equalAttrs(value, previousAttributes[key] ?? null)) { - delete changedAttributes[key] - } else { - changedAttributes[key] = value - } - if (value == null) { - delete currentAttributes[key] - } else { - currentAttributes[key] = value - } - } else if (freshDelete) { - if (equalAttrs(value, currAttrVal)) { - // nop - } else if (equalAttrs(currAttrVal, previousAttributes[key] ?? null)) { - delete changedAttributes[key] - } else { - changedAttributes[key] = currAttrVal - } - // current attributes doesn't change - previousAttributes[key] = value - - } else if (!c.deleted) { - // fresh reference to currentAttributes only - if (usingCurrentAttributes) { - currentAttributes = object.assign({}, currentAttributes) - usingCurrentAttributes = false - } - if (usingChangedAttributes && changedAttributes[key] !== undefined) { - usingChangedAttributes = false - changedAttributes = object.assign({}, changedAttributes) - } - if (value == null) { - delete currentAttributes[key] - } else { - currentAttributes[key] = value - } - delete changedAttributes[key] - previousAttributes[key] = value - } - // # Update Attributions - if (attribution != null) { - /** - * @type {import('../utils/Delta.js').Attribution} - */ - const formattingAttribution = object.assign({}, d.usedAttribution) - const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - if (value === null) { - delete attributesChanged[key] - } else { - const by = attributesChanged[key] = (attributesChanged[key]?.slice() ?? []) - by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) - const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt) formattingAttribution.attributedAt = attributedAt - } - if (object.isEmpty(attributesChanged)) { - d.useAttribution(null) - } else { - const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt != null) formattingAttribution.attributedAt = attributedAt - d.useAttribution(formattingAttribution) - } - } - break - } - } - } - } - }) - return d.done() - // const whatToWatch = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - // const genericDelta = this.target.getDelta(am, whatToWatch) - // if (!d.equals(genericDelta)) { - // console.log(d.toJSON()) - // console.log(genericDelta.toJSON()) - // debugger - // const d2 = this.target.getDelta(am, whatToWatch) - // throw new Error('should match', d2) - // } - // return d - // */ + return this.target.getDelta(am, whatToWatch) } /** @@ -966,34 +795,26 @@ export class YText extends AbstractType { * Apply a {@link Delta} on this shared YText type. * * @param {Array | delta.Delta} delta The changes to apply on this element. - * @param {object} opts - * @param {boolean} [opts.sanitize] Sanitize input delta. Removes ending newlines if set to true. - * + * @param {AbstractAttributionManager} am * * @public */ - applyDelta (delta, { sanitize = true } = {}) { + applyDelta (delta, am = noAttributionsManager) { if (this.doc !== null) { transact(this.doc, transaction => { /** * @type {Array} */ const deltaOps = /** @type {Array} */ (/** @type {delta.Delta} */ (delta).ops instanceof Array ? /** @type {delta.Delta} */ (delta).ops : delta) - const currPos = new ItemTextListPosition(null, this._start, 0, new Map()) + const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) for (let i = 0; i < deltaOps.length; i++) { const op = deltaOps[i] if (op.insert !== undefined) { - // Quill assumes that the content starts with an empty paragraph. - // Yjs/Y.Text assumes that it starts empty. We always hide that - // there is a newline at the end of the content. - // If we omit this step, clients will see a different number of - // paragraphs, but nothing bad will happen. - const ins = (!sanitize && typeof op.insert === 'string' && i === deltaOps.length - 1 && currPos.right === null && op.insert.slice(-1) === '\n') ? op.insert.slice(0, -1) : op.insert - if (typeof ins !== 'string' || ins.length > 0) { - insertText(transaction, this, currPos, ins, op.attributes || {}) + if (op.insert.length > 0 || typeof op.insert !== 'string') { + insertText(transaction, this, currPos, op.insert, op.attributes || {}) } } else if (op.retain !== undefined) { - formatText(transaction, this, currPos, op.retain, op.attributes || {}) + currPos.formatText(transaction, this, op.retain, op.attributes || {}) } else if (op.delete !== undefined) { deleteText(transaction, currPos, op.delete) } @@ -1182,7 +1003,11 @@ export class YText extends AbstractType { if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true - d.insert(c.content.getContent()[0], null, attribution) + if (!retainOnly) { + d.insert(c.content.getContent()[0], null, attribution) + } else { + d.retain(c.content.getLength(), null, attribution) + } } else if (renderDelete) { d.delete(1) } else if (retainContent) { @@ -1195,7 +1020,11 @@ export class YText extends AbstractType { if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true - d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) + if (!retainOnly) { + d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) + } else { + d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution) + } } else if (renderDelete) { d.delete(c.content.getLength()) } else if (retainContent) { @@ -1391,7 +1220,7 @@ export class YText extends AbstractType { if (pos.right === null) { return } - formatText(transaction, this, pos, length, attributes) + pos.formatText(transaction, this, length, attributes) }) } else { /** @type {Array} */ (this._pending).push(() => this.format(index, length, attributes)) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 95c6abaea..24c8e0270 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -5,14 +5,14 @@ import { createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, - Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line + Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line insertIntoIdMap, insertIntoIdSet, diffIdMap, createIdMap, createAttributionItem, mergeIdMaps, - createID + createID, } from '../internals.js' import * as error from 'lib0/error' @@ -106,6 +106,17 @@ export class AbstractAttributionManager { error.methodUnimplemented() } + /** + * Calculate the length of the attributed content. This is used by iterators that walk through the + * content. + * + * @param {Item} _item + * @return {number} + */ + contentLength (_item) { + error.methodUnimplemented() + } + destroy () {} } @@ -145,6 +156,18 @@ export class TwosetAttributionManager { } }) } + + /** + * @param {Item} item + * @return {number} + */ + contentLength (item) { + if (!item.deleted) { + return item.length + } else { + return this.deletes.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + } + } } /** @@ -168,6 +191,14 @@ export class NoAttributionsManager { contents.push(new AttributedContent(content, deleted, null, shouldRender)) } } + + /** + * @param {Item} item + * @return {number} + */ + contentLength (item) { + return item.deleted ? 0 : item.length + } } export const noAttributionsManager = new NoAttributionsManager() @@ -266,6 +297,18 @@ export class DiffAttributionManager { } }) } + + /** + * @param {Item} item + * @return {number} + */ + contentLength (item) { + if (!item.deleted) { + return item.length + } else { + return this.deletes.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + } + } } /** @@ -331,6 +374,14 @@ export class SnapshotAttributionManager { } }) } + + /** + * @param {Item} item + * @return {number} + */ + contentLength (item) { + return this.attrs.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + } } /** diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js index c4707a7c8..54a2d6863 100644 --- a/tests/attribution.tests.js +++ b/tests/attribution.tests.js @@ -32,3 +32,39 @@ export const testAttributedEvents = _tc => { ytext.insert(11, '!') t.assert(calledObserver) } + +/** + * @param {t.TestCase} _tc + */ +export const testInsertionsMindingAttributedContent = _tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + ytext.insert(0, 'hello world') + const v1 = Y.cloneDoc(ydoc) + ydoc.transact(() => { + ytext.delete(6, 5) + }) + let am = Y.createAttributionManagerFromDiff(v1, ydoc) + const c1 = ytext.getDelta(am) + t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) + ytext.applyDelta(delta.createTextDelta().retain(11).insert('content'), am) + t.assert(ytext.toString() === 'hello content') +} + +/** + * @param {t.TestCase} _tc + */ +export const testInsertionsIntoAttributedContent = _tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + ytext.insert(0, 'hello ') + const v1 = Y.cloneDoc(ydoc) + ydoc.transact(() => { + ytext.insert(6, 'word') + }) + let am = Y.createAttributionManagerFromDiff(v1, ydoc) + const c1 = ytext.getDelta(am) + t.compare(c1, delta.createTextDelta().insert('hello ').insert('word', null, { insert: [] })) + ytext.applyDelta(delta.createTextDelta().retain(9).insert('l'), am) + t.assert(ytext.toString() === 'hello world') +} From 43c752170d637e77d6ca68f6d1cdb6d5fead32ac Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 22 May 2025 19:46:27 +0200 Subject: [PATCH 316/362] fix reading content with new api --- src/index.js | 1 + src/types/AbstractType.js | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index 94a280128..5c2177072 100644 --- a/src/index.js +++ b/src/index.js @@ -113,6 +113,7 @@ export { createIdMapFromIdSet, TwosetAttributionManager, noAttributionsManager, + AbstractAttributionManager, iterateStructsByIdSet, createAttributionManagerFromDiff, createIdSet, diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 621190356..57b3aa6fb 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -514,7 +514,7 @@ export const typeListGetContent = (type, am) => { for (let item = type._start; item !== null; cs.length = 0) { // populate cs for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) } for (let i = 0; i < cs.length; i++) { const c = cs[i] @@ -1011,7 +1011,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const cs = [] - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, !item.deleted) + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) const { attribution } = createAttributionFromAttributionItems(attrs, deleted) @@ -1027,7 +1027,7 @@ export const typeMapGetContent = (parent, am) => { * @type {Array>} */ const tmpcs = [] - am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, !prevItem.deleted) + am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, 1) cs = tmpcs.concat(cs) if (cs.length === 0 || cs[0].attrs == null) { cs.splice(0, cs.findIndex(c => c.attrs != null)) From 34b90fcdd6350e3c09e8ec5c7c11e663a90ee9ca Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 24 May 2025 01:14:21 +0200 Subject: [PATCH 317/362] [attribution] fixes for suggestion support in y-quill --- README.md | 2 + src/index.js | 1 + src/types/YText.js | 118 +++++++------------------------- src/utils/AttributionManager.js | 42 ++++++++---- src/utils/Delta.js | 2 +- tests/attribution.tests.js | 6 +- 6 files changed, 57 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index da364b6c5..93adbb70f 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,8 @@ editing for your IDE or custom editor * [Lightpage](https://lightpage.com/) - Personal living notebook * [reearth-flow](https://github.com/reearth/reearth-flow) - Collaboratively calculate and convert various data +* [ProtonMail | Proton Docs](https://proton.me/drive/docs) - E2E encrypted + collaborative documents in Proton Drive. ## Table of Contents diff --git a/src/index.js b/src/index.js index 5c2177072..691e3efc0 100644 --- a/src/index.js +++ b/src/index.js @@ -117,6 +117,7 @@ export { iterateStructsByIdSet, createAttributionManagerFromDiff, createIdSet, + mergeIdSets, cloneDoc } from './internals.js' diff --git a/src/types/YText.js b/src/types/YText.js index 5ef624190..02729f788 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -130,13 +130,14 @@ export class ItemTextListPosition { } break } - default: + default: { const rightLen = this.am.contentLength(this.right) if (length < rightLen) { getItemCleanStart(transaction, createID(this.right.id.client, this.right.id.clock + length)) } length -= rightLen break + } } this.forward() } @@ -648,8 +649,8 @@ export class YTextEvent extends YEvent { * @public */ getDelta (am = noAttributionsManager) { - const whatToWatch = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - return this.target.getDelta(am, whatToWatch) + const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) + return this.target.getDelta(am, { itemsToRender, retainDeletes: true }) } /** @@ -846,12 +847,6 @@ export class YText extends AbstractType { } /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * * @param {AbstractAttributionManager} am * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * @@ -859,92 +854,25 @@ export class YText extends AbstractType { */ getContent (am = noAttributionsManager) { return this.getDelta(am) - // this.doc ?? warnPrematureAccess() - // /** - // * @type {delta.TextDelta} - // */ - // const d = delta.createTextDelta() - // /** - // * @type {Array>} - // */ - // const cs = [] - // for (let item = this._start; item !== null; cs.length = 0) { - // // populate cs - // for (; item !== null && cs.length < 50; item = item.right) { - // am.readContent(cs, item, false) - // } - // for (let i = 0; i < cs.length; i++) { - // const { content, deleted, attrs } = cs[i] - // const { attribution, retainOnly } = createAttributionFromAttributionItems(attrs, deleted) - // switch (content.constructor) { - // case ContentString: { - // if (retainOnly) { - // d.retain(content.getLength(), null, attribution) - // } else { - // d.insert(/** @type {ContentString} */ (content).str, null, attribution) - // } - // break - // } - // case ContentType: - // case ContentEmbed: { - // if (retainOnly) { - // d.retain(content.getLength(), null, attribution) - // } else { - // d.insert(/** @type {ContentEmbed | ContentType} */ (content).getContent()[0], null, attribution) - // } - // break - // } - // case ContentFormat: { - // const contentFormat = /** @type {ContentFormat} */ (content) - // if (attribution != null) { - // /** - // * @type {import('../utils/Delta.js').Attribution} - // */ - // const formattingAttribution = object.assign({}, d.usedAttribution) - // const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - // if (contentFormat.value === null) { - // delete attributesChanged[contentFormat.key] - // } else { - // const by = attributesChanged[contentFormat.key] = attributesChanged[contentFormat.key]?.slice() ?? [] - // by.push(...((deleted ? attribution.delete : attribution.insert) ?? [])) - // const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) - // if (attributedAt) formattingAttribution.attributedAt = attributedAt - // } - // if (object.isEmpty(attributesChanged)) { - // d.useAttribution(null) - // } else { - // const attributedAt = (deleted ? attribution.deletedAt : attribution.insertedAt) - // if (attributedAt != null) formattingAttribution.attributedAt = attributedAt - // d.useAttribution(formattingAttribution) - // } - // } - // if (!deleted) { - // const currAttrs = d.usedAttributes - // if (contentFormat.value == null) { - // const nextAttrs = object.assign({}, currAttrs) - // delete nextAttrs[contentFormat.key] - // d.useAttributes(nextAttrs) - // } else { - // d.useAttributes(object.assign({}, currAttrs, { [contentFormat.key]: contentFormat.value })) - // } - // } - // break - // } - // } - // } - // } - // return d } /** + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * * @param {AbstractAttributionManager} am - * @param {import('../utils/IdSet.js').IdSet?} itemsToRender - * @param {boolean} retainOnly - if true, retain the rendered items with attributes and attributions. + * @param {Object} [opts] + * @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender] + * @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions + * @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * * @public */ - getDelta (am = noAttributionsManager, itemsToRender = null, retainOnly = false) { + getDelta (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false } = {}) { /** * @type {import('../utils/Delta.js').TextDelta} */ @@ -972,7 +900,7 @@ export class YText extends AbstractType { if (itemsToRender != null) { for (; item !== null && cs.length < 50; item = item.right) { const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length) - let itemContent = rslice.length > 1 ? item.content.copy() : item.content + const itemContent = rslice.length > 1 ? item.content.copy() : item.content for (let ir = 0; ir < rslice.length; ir++) { const idrange = rslice[ir] const content = itemContent @@ -1003,10 +931,10 @@ export class YText extends AbstractType { if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true - if (!retainOnly) { - d.insert(c.content.getContent()[0], null, attribution) - } else { + if (c.deleted ? retainDeletes : retainInserts) { d.retain(c.content.getLength(), null, attribution) + } else { + d.insert(c.content.getContent()[0], null, attribution) } } else if (renderDelete) { d.delete(1) @@ -1020,10 +948,10 @@ export class YText extends AbstractType { if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true - if (!retainOnly) { - d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) - } else { + if (c.deleted ? retainDeletes : retainInserts) { d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution) + } else { + d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) } } else if (renderDelete) { d.delete(c.content.getLength()) @@ -1302,7 +1230,7 @@ export class YText extends AbstractType { } /** - * @param {this} other + * @param {this} other */ [traits.EqualityTraitSymbol] (other) { return this.getContent().equals(other.getContent()) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 24c8e0270..817d86cfe 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -5,7 +5,6 @@ import { createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, - Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line insertIntoIdMap, insertIntoIdSet, diffIdMap, @@ -13,9 +12,12 @@ import { createAttributionItem, mergeIdMaps, createID, + mergeIdSets, + IdSet, Item, Snapshot, Doc, AbstractContent, IdMap // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' +import { ObservableV2 } from 'lib0/observable' /** * @typedef {Object} Attribution @@ -92,8 +94,13 @@ export class AttributedContent { /** * Abstract class for associating Attributions to content / changes + * + * Should fire an event when the attributions changed _after_ the original change happens. This + * Event will be used to update the attribution on the current content. + * + * @extends {ObservableV2<{change:(idset:IdSet,origin:any,local:boolean)=>void}>} */ -export class AbstractAttributionManager { +export class AbstractAttributionManager extends ObservableV2 { /** * @param {Array>} _contents - where to write the result * @param {number} _client @@ -116,25 +123,24 @@ export class AbstractAttributionManager { contentLength (_item) { error.methodUnimplemented() } - - destroy () {} } /** * @implements AbstractAttributionManager + * + * @extends {ObservableV2<{change:(idset:IdSet,origin:any,local:boolean)=>void}>} */ -export class TwosetAttributionManager { +export class TwosetAttributionManager extends ObservableV2 { /** * @param {IdMap} inserts * @param {IdMap} deletes */ constructor (inserts, deletes) { + super() this.inserts = inserts this.deletes = deletes } - destroy () {} - /** * @param {Array>} contents - where to write the result * @param {number} client @@ -174,10 +180,10 @@ export class TwosetAttributionManager { * Abstract class for associating Attributions to content / changes * * @implements AbstractAttributionManager + * + * @extends {ObservableV2<{change:(idset:IdSet,origin:any,local:boolean)=>void}>} */ -export class NoAttributionsManager { - destroy () {} - +export class NoAttributionsManager extends ObservableV2 { /** * @param {Array>} contents - where to write the result * @param {number} _client @@ -205,13 +211,16 @@ export const noAttributionsManager = new NoAttributionsManager() /** * @implements AbstractAttributionManager + * + * @extends {ObservableV2<{change:(idset:IdSet,origin:any,local:boolean)=>void}>} */ -export class DiffAttributionManager { +export class DiffAttributionManager extends ObservableV2 { /** * @param {Doc} prevDoc * @param {Doc} nextDoc */ constructor (prevDoc, nextDoc) { + super() const _nextDocInserts = createInsertionSetFromStructStore(nextDoc.store, false) // unmaintained const _prevDocInserts = createInsertionSetFromStructStore(prevDoc.store, false) // unmaintained const nextDocDeletes = createDeleteSetFromStructStore(nextDoc.store) // maintained @@ -227,7 +236,7 @@ export class DiffAttributionManager { const diffInserts = diffIdSet(tr.insertSet, _prevDocInserts) insertIntoIdMap(this.inserts, createIdMapFromIdSet(diffInserts, [])) // update deletes - const diffDeletes = diffIdSet(tr.deleteSet, prevDocDeletes) + const diffDeletes = diffIdSet(diffIdSet(tr.deleteSet, prevDocDeletes), this.inserts) insertIntoIdMap(this.deletes, createIdMapFromIdSet(diffDeletes, [])) // @todo fire update ranges on `diffInserts` and `diffDeletes` }) @@ -249,12 +258,14 @@ export class DiffAttributionManager { this.deletes = diffIdMap(this.deletes, tr.deleteSet) } // @todo fire update ranges on `tr.insertSet` and `tr.deleteSet` + this.emit('change', [mergeIdSets([tr.insertSet, tr.deleteSet]), tr.origin, tr.local]) }) this._destroyHandler = nextDoc.on('destroy', this.destroy.bind(this)) prevDoc.on('destroy', this._destroyHandler) } destroy () { + super.destroy() this._nextDoc.off('destroy', this._destroyHandler) this._prevDoc.off('destroy', this._destroyHandler) this._nextDoc.off('beforeObserverCalls', this._nextBOH) @@ -324,13 +335,16 @@ export const createAttributionManagerFromDiff = (prevDoc, nextDoc) => new DiffAt * read content similar to the previous snapshot api. Requires that `ydoc.gc` is turned off. * * @implements AbstractAttributionManager + * + * @extends {ObservableV2<{change:(idset:IdSet,origin:any,local:boolean)=>void}>} */ -export class SnapshotAttributionManager { +export class SnapshotAttributionManager extends ObservableV2 { /** * @param {Snapshot} prevSnapshot * @param {Snapshot} nextSnapshot */ constructor (prevSnapshot, nextSnapshot) { + super() this.prevSnapshot = prevSnapshot this.nextSnapshot = nextSnapshot const inserts = createIdMap() @@ -343,8 +357,6 @@ export class SnapshotAttributionManager { this.attrs = mergeIdMaps([diffIdMap(inserts, prevSnapshot.ds), deletes]) } - destroy () { } - /** * @param {Array>} contents - where to write the result * @param {number} client diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 6f820667a..362a6aaa9 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -413,7 +413,7 @@ export class DeltaBuilder extends AbstractDelta { * @return {this} */ done () { - while (this.lastOp != null && this.lastOp instanceof RetainOp && this.lastOp.attributes === null) { + while (this.lastOp != null && this.lastOp instanceof RetainOp && this.lastOp.attributes === null && this.lastOp.attribution === null) { this.ops.pop() this.lastOp = this.ops[this.ops.length - 1] ?? null } diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js index 54a2d6863..c6bbeea28 100644 --- a/tests/attribution.tests.js +++ b/tests/attribution.tests.js @@ -20,7 +20,7 @@ export const testAttributedEvents = _tc => { ydoc.transact(() => { ytext.delete(6, 5) }) - let am = Y.createAttributionManagerFromDiff(v1, ydoc) + const am = Y.createAttributionManagerFromDiff(v1, ydoc) const c1 = ytext.getDelta(am) t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) let calledObserver = false @@ -44,7 +44,7 @@ export const testInsertionsMindingAttributedContent = _tc => { ydoc.transact(() => { ytext.delete(6, 5) }) - let am = Y.createAttributionManagerFromDiff(v1, ydoc) + const am = Y.createAttributionManagerFromDiff(v1, ydoc) const c1 = ytext.getDelta(am) t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) ytext.applyDelta(delta.createTextDelta().retain(11).insert('content'), am) @@ -62,7 +62,7 @@ export const testInsertionsIntoAttributedContent = _tc => { ydoc.transact(() => { ytext.insert(6, 'word') }) - let am = Y.createAttributionManagerFromDiff(v1, ydoc) + const am = Y.createAttributionManagerFromDiff(v1, ydoc) const c1 = ytext.getDelta(am) t.compare(c1, delta.createTextDelta().insert('hello ').insert('word', null, { insert: [] })) ytext.applyDelta(delta.createTextDelta().retain(9).insert('l'), am) From 90514dd51b047febe5e9689cd9778c660191cf31 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 24 May 2025 12:56:37 +0200 Subject: [PATCH 318/362] more attribution fixes for y-quill --- src/types/AbstractType.js | 4 +-- src/types/YText.js | 17 ++++++++----- src/utils/AttributionManager.js | 43 +++++++++++++++++---------------- tests/y-xml.tests.js | 6 ++--- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 57b3aa6fb..ce453e8ae 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -518,7 +518,7 @@ export const typeListGetContent = (type, am) => { } for (let i = 0; i < cs.length; i++) { const c = cs[i] - const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted).attribution + const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted) if (c.content.isCountable()) { if (c.render || attribution != null) { d.insert(c.content.getContent(), null, attribution) @@ -1014,7 +1014,7 @@ export const typeMapGetContent = (parent, am) => { am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) const { deleted, attrs, content } = cs[cs.length - 1] const c = array.last(content.getContent()) - const { attribution } = createAttributionFromAttributionItems(attrs, deleted) + const attribution = createAttributionFromAttributionItems(attrs, deleted) if (deleted) { mapcontent[key] = { prevValue: c, value: undefined, attribution } } else { diff --git a/src/types/YText.js b/src/types/YText.js index 02729f788..2fbb1d8c3 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -27,7 +27,8 @@ import { noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line createAttributionFromAttributionItems, mergeIdSets, - diffIdSet + diffIdSet, + ContentDeleted } from '../internals.js' import * as delta from '../utils/Delta.js' @@ -900,12 +901,12 @@ export class YText extends AbstractType { if (itemsToRender != null) { for (; item !== null && cs.length < 50; item = item.right) { const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length) - const itemContent = rslice.length > 1 ? item.content.copy() : item.content + let itemContent = rslice.length > 1 ? item.content.copy() : item.content for (let ir = 0; ir < rslice.length; ir++) { const idrange = rslice[ir] const content = itemContent if (ir !== rslice.length - 1) { - itemContent.splice(idrange.len) + itemContent = itemContent.splice(idrange.len) } am.readContent(cs, item.id.client, idrange.clock, item.deleted, content, idrange.exists ? 2 : 0) } @@ -924,15 +925,19 @@ export class YText extends AbstractType { const renderDelete = c.render && c.deleted // existing content that should be retained, only adding changed attributes const retainContent = !c.render && (!c.deleted || c.attrs != null) - const attribution = renderContent ? createAttributionFromAttributionItems(c.attrs, c.deleted).attribution : null + const attribution = renderContent ? createAttributionFromAttributionItems(c.attrs, c.deleted) : null switch (c.content.constructor) { + case ContentDeleted: { + if (renderDelete) d.delete(c.content.getLength()) + break + } case ContentType: case ContentEmbed: if (renderContent) { d.usedAttributes = currentAttributes usingCurrentAttributes = true if (c.deleted ? retainDeletes : retainInserts) { - d.retain(c.content.getLength(), null, attribution) + d.retain(c.content.getLength(), null, attribution ?? {}) } else { d.insert(c.content.getContent()[0], null, attribution) } @@ -949,7 +954,7 @@ export class YText extends AbstractType { d.usedAttributes = currentAttributes usingCurrentAttributes = true if (c.deleted ? retainDeletes : retainInserts) { - d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution) + d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution ?? null) } else { d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) } diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 817d86cfe..573ea133f 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -13,18 +13,23 @@ import { mergeIdMaps, createID, mergeIdSets, - IdSet, Item, Snapshot, Doc, AbstractContent, IdMap // eslint-disable-line + IdSet, Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line + intersectSets } from '../internals.js' import * as error from 'lib0/error' import { ObservableV2 } from 'lib0/observable' /** + * @todo rename this to `insertBy`, `insertAt`, .. + * * @typedef {Object} Attribution * @property {Array} [Attribution.insert] * @property {number} [Attribution.insertedAt] - * @property {Array} [Attribution.suggest] - * @property {number} [Attribution.suggestedAt] + * @property {Array} [Attribution.acceptInsert] + * @property {number} [Attribution.acceptedDeleteAt] + * @property {Array} [Attribution.acceptDelete] + * @property {number} [Attribution.acceptedDeleteAt] * @property {Array} [Attribution.delete] * @property {number} [Attribution.deletedAt] * @property {{ [key: string]: Array }} [Attribution.attributes] @@ -35,16 +40,15 @@ import { ObservableV2 } from 'lib0/observable' * @todo SHOULD NOT RETURN AN OBJECT! * @param {Array>?} attrs * @param {boolean} deleted - whether the attributed item is deleted - * @return {{ attribution: Attribution?, retainOnly: boolean }} + * @return {Attribution?} */ export const createAttributionFromAttributionItems = (attrs, deleted) => { + if (attrs == null) return null /** - * @type {Attribution?} + * @type {Attribution} */ - let attribution = null - let retainOnly = false + const attribution = {} if (attrs != null) { - attribution = {} if (deleted) { attribution.delete = [] } else { @@ -52,12 +56,14 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { } attrs.forEach(attr => { switch (attr.name) { - case 'retain': - retainOnly = true - break + case 'acceptDelete': + delete attribution.delete + // eslint-disable-next-line no-fallthrough + case 'acceptInsert': + delete attribution.insert + // eslint-disable-next-line no-fallthrough case 'insert': - case 'delete': - case 'suggest': { + case 'delete': { const as = /** @type {import('../utils/Delta.js').Attribution} */ (attribution) const ls = as[attr.name] = as[attr.name] ?? [] ls.push(attr.val) @@ -71,7 +77,7 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { } }) } - return { attribution, retainOnly } + return attribution } /** @@ -243,13 +249,8 @@ export class DiffAttributionManager extends ObservableV2 { this._prevBOH = prevDoc.on('beforeObserverCalls', tr => { insertIntoIdSet(_prevDocInserts, tr.insertSet) insertIntoIdSet(prevDocDeletes, tr.deleteSet) - if (tr.insertSet.clients.size < 2) { - tr.insertSet.forEach((idrange, client) => { - this.inserts.delete(client, idrange.clock, idrange.len) - }) - } else { - this.inserts = diffIdMap(this.inserts, tr.insertSet) - } + insertIntoIdMap(this.inserts, createIdMapFromIdSet(intersectSets(tr.insertSet, this.inserts), [createAttributionItem('acceptInsert', 'unknown')])) + // insertIntoIdMap(this.deletes, createIdMapFromIdSet(intersectSets(tr.deleteSet, this.deletes), [createAttributionItem('acceptDelete', 'unknown')])) if (tr.deleteSet.clients.size < 2) { tr.deleteSet.forEach((attrRange, client) => { this.deletes.delete(client, attrRange.clock, attrRange.len) diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 44a4eb845..48fe8f37f 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -364,14 +364,14 @@ export const testElementAttributedContentViaDiffer = _tc => { t.group('test getContentDeep both docs synced', () => { t.info('expecting diffingAttributionManager to auto update itself') const expectedContent = delta.createArrayDelta().insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]).insert([ - delta.createTextDelta().insert('bigworld') - ]) + delta.createTextDelta().insert('bigworld', null, { acceptInsert: ['unknown'] }) + ], null, { acceptInsert: ['unknown'] }) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: null } }) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { acceptInsert: ['unknown'] } } }) t.assert(attributedContent.nodeName === 'UNDEFINED') }) } From f1ae2a78a143408aaeabc62a52e87b1c4265242a Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 29 May 2025 21:47:29 +0200 Subject: [PATCH 319/362] suggestion fixes --- src/types/YText.js | 22 +++++- src/utils/AttributionManager.js | 134 +++++++++++++++++++++++--------- src/utils/Delta.js | 5 +- src/utils/RelativePosition.js | 27 +++---- tests/attribution.tests.js | 18 +++++ 5 files changed, 150 insertions(+), 56 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 2fbb1d8c3..ace4c6c58 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -28,6 +28,7 @@ import { createAttributionFromAttributionItems, mergeIdSets, diffIdSet, + createIdSet, ContentDeleted } from '../internals.js' @@ -230,7 +231,7 @@ const insertNegatedAttributes = (transaction, parent, currPos, negatedAttributes // check if we really need to remove attributes while ( currPos.right !== null && ( - currPos.right.deleted === true || ( + (currPos.right.deleted && (currPos.am == noAttributionsManager || currPos.am.contentLength(currPos.right) === 0)) || ( currPos.right.content.constructor === ContentFormat && equalAttrs(negatedAttributes.get(/** @type {ContentFormat} */ (currPos.right.content).key), /** @type {ContentFormat} */ (currPos.right.content).value) ) @@ -281,7 +282,7 @@ const minimizeAttributeChanges = (currPos, attributes) => { while (true) { if (currPos.right === null) { break - } else if (currPos.right.deleted || (currPos.right.content.constructor === ContentFormat && equalAttrs(attributes[(/** @type {ContentFormat} */ (currPos.right.content)).key] ?? null, /** @type {ContentFormat} */ (currPos.right.content).value))) { + } else if (currPos.right.deleted ? (currPos.am.contentLength(currPos.right) === 0) : (!currPos.right.deleted && currPos.right.content.constructor === ContentFormat && equalAttrs(attributes[(/** @type {ContentFormat} */ (currPos.right.content)).key] ?? null, /** @type {ContentFormat} */ (currPos.right.content).value))) { // } else { break @@ -539,7 +540,7 @@ const deleteText = (transaction, currPos, length) => { const startAttrs = map.copy(currPos.currentAttributes) const start = currPos.right while (length > 0 && currPos.right !== null) { - if (currPos.right.deleted === false) { + if (!currPos.right.deleted) { switch (currPos.right.content.constructor) { case ContentType: case ContentEmbed: @@ -551,6 +552,19 @@ const deleteText = (transaction, currPos, length) => { currPos.right.delete(transaction) break } + } else if (currPos.am !== noAttributionsManager) { + const item = currPos.right + let d = currPos.am.contentLength(item) + if (d > 0) { + if (length < d) { + getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length)) + d = length + } + // deleting already deleted content. store that information in a meta property, but do + // nothing + map.setIfUndefined(transaction.meta, 'attributedDeletes', createIdSet).add(item.id.client, item.id.clock, d) + length -= d + } } currPos.forward() } @@ -954,7 +968,7 @@ export class YText extends AbstractType { d.usedAttributes = currentAttributes usingCurrentAttributes = true if (c.deleted ? retainDeletes : retainInserts) { - d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution ?? null) + d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution ?? {}) } else { d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) } diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 573ea133f..ad78a6fe6 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -14,11 +14,15 @@ import { createID, mergeIdSets, IdSet, Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line - intersectSets + applyUpdate, + writeIdSet, + UpdateEncoderV1, + transact } from '../internals.js' import * as error from 'lib0/error' import { ObservableV2 } from 'lib0/observable' +import * as encoding from 'lib0/encoding' /** * @todo rename this to `insertBy`, `insertAt`, .. @@ -48,35 +52,33 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { * @type {Attribution} */ const attribution = {} - if (attrs != null) { - if (deleted) { - attribution.delete = [] - } else { - attribution.insert = [] - } - attrs.forEach(attr => { - switch (attr.name) { - case 'acceptDelete': - delete attribution.delete - // eslint-disable-next-line no-fallthrough - case 'acceptInsert': - delete attribution.insert - // eslint-disable-next-line no-fallthrough - case 'insert': - case 'delete': { - const as = /** @type {import('../utils/Delta.js').Attribution} */ (attribution) - const ls = as[attr.name] = as[attr.name] ?? [] - ls.push(attr.val) - break - } - default: { - if (attr.name[0] !== '_') { - /** @type {any} */ (attribution)[attr.name] = attr.val - } + if (deleted) { + attribution.delete = [] + } else { + attribution.insert = [] + } + attrs.forEach(attr => { + switch (attr.name) { + case 'acceptDelete': + delete attribution.delete + // eslint-disable-next-line no-fallthrough + case 'acceptInsert': + delete attribution.insert + // eslint-disable-next-line no-fallthrough + case 'insert': + case 'delete': { + const as = /** @type {import('../utils/Delta.js').Attribution} */ (attribution) + const ls = as[attr.name] = as[attr.name] ?? [] + ls.push(attr.val) + break + } + default: { + if (attr.name[0] !== '_') { + /** @type {any} */ (attribution)[attr.name] = attr.val } } - }) - } + } + }) return attribution } @@ -123,6 +125,8 @@ export class AbstractAttributionManager extends ObservableV2 { * Calculate the length of the attributed content. This is used by iterators that walk through the * content. * + * If the content is not countable, it should return 0. + * * @param {Item} _item * @return {number} */ @@ -174,7 +178,9 @@ export class TwosetAttributionManager extends ObservableV2 { * @return {number} */ contentLength (item) { - if (!item.deleted) { + if (!item.content.isCountable()) { + return 0 + } else if (!item.deleted) { return item.length } else { return this.deletes.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) @@ -209,7 +215,7 @@ export class NoAttributionsManager extends ObservableV2 { * @return {number} */ contentLength (item) { - return item.deleted ? 0 : item.length + return (item.deleted || !item.content.isCountable()) ? 0 : item.length } } @@ -249,7 +255,14 @@ export class DiffAttributionManager extends ObservableV2 { this._prevBOH = prevDoc.on('beforeObserverCalls', tr => { insertIntoIdSet(_prevDocInserts, tr.insertSet) insertIntoIdSet(prevDocDeletes, tr.deleteSet) - insertIntoIdMap(this.inserts, createIdMapFromIdSet(intersectSets(tr.insertSet, this.inserts), [createAttributionItem('acceptInsert', 'unknown')])) + // insertIntoIdMap(this.inserts, createIdMapFromIdSet(intersectSets(tr.insertSet, this.inserts), [createAttributionItem('acceptInsert', 'unknown')])) + if (tr.insertSet.clients.size < 2) { + tr.insertSet.forEach((attrRange, client) => { + this.inserts.delete(client, attrRange.clock, attrRange.len) + }) + } else { + this.inserts = diffIdMap(this.inserts, tr.insertSet) + } // insertIntoIdMap(this.deletes, createIdMapFromIdSet(intersectSets(tr.deleteSet, this.deletes), [createAttributionItem('acceptDelete', 'unknown')])) if (tr.deleteSet.clients.size < 2) { tr.deleteSet.forEach((attrRange, client) => { @@ -261,6 +274,41 @@ export class DiffAttributionManager extends ObservableV2 { // @todo fire update ranges on `tr.insertSet` and `tr.deleteSet` this.emit('change', [mergeIdSets([tr.insertSet, tr.deleteSet]), tr.origin, tr.local]) }) + // changes from prevDoc should always flow into suggestionDoc + // changes from suggestionDoc only flow into ydoc if suggestion-mode is disabled + this._prevUpdateListener = prevDoc.on('update', (update, origin) => { + origin !== this && applyUpdate(nextDoc, update) + }) + this._ndUpdateListener = nextDoc.on('update', (update, origin, _doc, tr) => { + // only if event is local and suggestion mode is enabled + if (!this.suggestionMode && tr.local && (this.suggestionOrigins == null || this.suggestionOrigins.some(o => o === origin))) { + applyUpdate(prevDoc, update, this) + } + }) + this._afterTrListener = nextDoc.on('afterTransaction', (tr) => { + // apply deletes on attributed deletes (content that is already deleted, but is rendered by + // the attribution manager) + if (!this.suggestionMode && tr.local && (this.suggestionOrigins == null || this.suggestionOrigins.some(o => o === origin))) { + const attributedDeletes = tr.meta.get('attributedDeletes') + if (attributedDeletes != null) { + transact(prevDoc, () => { + // apply attributed deletes if there are any + const ds = new UpdateEncoderV1() + encoding.writeVarUint(ds.restEncoder, 0) // encode 0 structs + writeIdSet(ds, attributedDeletes) + applyUpdate(prevDoc, ds.toUint8Array()) + }, this) + } + } + }) + this.suggestionMode = true + /** + * Optionally limit origins that may sync changes to the main doc if suggestion-mode is + * disabled. + * + * @type {Array?} + */ + this.suggestionOrigins = null this._destroyHandler = nextDoc.on('destroy', this.destroy.bind(this)) prevDoc.on('destroy', this._destroyHandler) } @@ -271,6 +319,9 @@ export class DiffAttributionManager extends ObservableV2 { this._prevDoc.off('destroy', this._destroyHandler) this._nextDoc.off('beforeObserverCalls', this._nextBOH) this._prevDoc.off('beforeObserverCalls', this._prevBOH) + this._prevDoc.off('update', this._prevUpdateListener) + this._nextDoc.off('update', this._ndUpdateListener) + this._nextDoc.off('afterTransaction', this._afterTrListener) } /** @@ -316,10 +367,18 @@ export class DiffAttributionManager extends ObservableV2 { */ contentLength (item) { if (!item.deleted) { - return item.length - } else { - return this.deletes.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + return item.content.isCountable() ? item.length : 0 + } + const slice = this.deletes.sliceId(item.id, item.length) + let content = item.content + if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.hasId(item.id)) { + const prevItem = getItem(this._prevDocStore, item.id) + content = prevItem.content + } + if (!content.isCountable()) { + return 0 } + return slice.reduce((len, s) => s.attrs != null ? len + s.len : len, 0) } } @@ -393,7 +452,12 @@ export class SnapshotAttributionManager extends ObservableV2 { * @return {number} */ contentLength (item) { - return this.attrs.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + return item.content.isCountable() + ? (item.deleted + ? this.attrs.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + : item.length + ) + : 0 } } diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 362a6aaa9..5352bac1f 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -268,10 +268,7 @@ export class AbstractDelta { * @param {T | null} a * @param {T | null} b */ -const mergeAttrs = (a, b) => { - const merged = object.isEmpty(a) ? b : (object.isEmpty(b) ? a : object.assign({}, a, b)) - return object.isEmpty(merged) ? null : merged -} +const mergeAttrs = (a, b) => object.isEmpty(a) ? b : (object.isEmpty(b) ? a : object.assign({}, a, b)) /** * @template {'array' | 'text' | 'custom'} Type diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index 4f6ced71e..3dd43ccde 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -9,7 +9,8 @@ import { ContentType, followRedone, getItem, - StructStore, ID, Doc, AbstractType, // eslint-disable-line + StructStore, ID, Doc, AbstractType, + noAttributionsManager, // eslint-disable-line } from '../internals.js' import * as encoding from 'lib0/encoding' @@ -72,6 +73,7 @@ export class RelativePosition { * @type {number} */ this.assoc = assoc + this.item && console.log('created relpos', this.item) // @todo remove } } @@ -156,11 +158,12 @@ export const createRelativePosition = (type, item, assoc) => { * @param {AbstractType} type The base type (e.g. YText or YArray). * @param {number} index The absolute position. * @param {number} [assoc] + * @param {import('../utils/AttributionManager.js').AbstractAttributionManager} attributionManager * @return {RelativePosition} * * @function */ -export const createRelativePositionFromTypeIndex = (type, index, assoc = 0) => { +export const createRelativePositionFromTypeIndex = (type, index, assoc = 0, attributionManager = noAttributionsManager) => { let t = type._start if (assoc < 0) { // associated to the left character or the beginning of a type, increment index if possible. @@ -170,13 +173,12 @@ export const createRelativePositionFromTypeIndex = (type, index, assoc = 0) => { index-- } while (t !== null) { - if (!t.deleted && t.countable) { - if (t.length > index) { - // case 1: found position somewhere in the linked list - return createRelativePosition(type, createID(t.id.client, t.id.clock + index), assoc) - } - index -= t.length + const len = attributionManager.contentLength(t) + if (len > index) { + // case 1: found position somewhere in the linked list + return createRelativePosition(type, createID(t.id.client, t.id.clock + index), assoc) } + index -= len if (t.right === null && assoc < 0) { // left-associated position, return last available id return createRelativePosition(type, t.lastId, assoc) @@ -282,11 +284,12 @@ const getItemWithOffset = (store, id) => { * @param {RelativePosition} rpos * @param {Doc} doc * @param {boolean} followUndoneDeletions - whether to follow undone deletions - see https://github.com/yjs/yjs/issues/638 + * @param {import('../utils/AttributionManager.js').AbstractAttributionManager} attributionManager * @return {AbsolutePosition|null} * * @function */ -export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndoneDeletions = true) => { +export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndoneDeletions = true, attributionManager = noAttributionsManager) => { const store = doc.store const rightID = rpos.item const typeID = rpos.type @@ -305,12 +308,10 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc, followUndo } type = /** @type {AbstractType} */ (right.parent) if (type._item === null || !type._item.deleted) { - index = (right.deleted || !right.countable) ? 0 : (res.diff + (assoc >= 0 ? 0 : 1)) // adjust position based on left association if necessary + index = attributionManager.contentLength(right) === 0 ? 0 : (res.diff + (assoc >= 0 ? 0 : 1)) // adjust position based on left association if necessary let n = right.left while (n !== null) { - if (!n.deleted && n.countable) { - index += n.length - } + index += attributionManager.contentLength(n) n = n.left } } diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js index c6bbeea28..71ee0c1ca 100644 --- a/tests/attribution.tests.js +++ b/tests/attribution.tests.js @@ -9,6 +9,24 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' import * as delta from '../src/utils/Delta.js' +/** + * @param {t.TestCase} _tc + */ +export const testRelativePositions = _tc => { + const ydoc = new Y.Doc() + const ytext = ydoc.getText() + ytext.insert(0, 'hello world') + const v1 = Y.cloneDoc(ydoc) + ytext.delete(1, 6) + ytext.insert(1, 'x', ) + const am = Y.createAttributionManagerFromDiff(v1, ydoc) + const rel = Y.createRelativePositionFromTypeIndex(ytext, 9, 1, am) // pos after "hello wo" + const abs1 = Y.createAbsolutePositionFromRelativePosition(rel, ydoc, true, am) + const abs2 = Y.createAbsolutePositionFromRelativePosition(rel, ydoc, true) + t.assert(abs1?.index === 9) + t.assert(abs2?.index === 3) +} + /** * @param {t.TestCase} _tc */ From 172b157b7c994c717a4e458b154f036657c8156b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 30 May 2025 18:50:39 +0200 Subject: [PATCH 320/362] fix puzzle1 --- src/utils/AttributionManager.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index ad78a6fe6..9073396aa 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -335,22 +335,18 @@ export class DiffAttributionManager extends ObservableV2 { readContent (contents, client, clock, deleted, content, shouldRender) { const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, content.getLength()) content = slice.length === 1 ? content : content.copy() - if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.has(client, clock)) { - // Retrieved item is never more fragmented than the newer item. - const prevItem = getItem(this._prevDocStore, createID(client, clock)) - const originalContentLen = content.getLength() - content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content - // trim itemContent to the correct size. - const diffStart = clock - prevItem.id.clock - const diffEnd = prevItem.id.clock + prevItem.length - clock - originalContentLen - if (diffStart > 0) { - content = content.splice(diffStart) - } - if (diffEnd > 0) { - content.splice(content.getLength() - diffEnd) - } - } slice.forEach(s => { + if (content.getLength() === 0 || (content instanceof ContentDeleted && (s.attrs != null || shouldRender) && !this.inserts.has(client, s.clock))) { // @todo possibly remove this.inserts.. + // Retrieved item is never more fragmented than the newer item. + const prevItem = getItem(this._prevDocStore, createID(client, s.clock)) + const originalContentLen = content.getLength() + content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content + // trim itemContent to the correct size. + const diffStart = s.clock - prevItem.id.clock + if (diffStart > 0) { + content = content.splice(diffStart) + } + } const c = content if (s.len < c.getLength()) { content = c.splice(s.len) From d9490c43d14caa3edd944a7f0005d4fc5ec398b9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 31 May 2025 21:00:30 +0200 Subject: [PATCH 321/362] fix issue with slicing content in attrMngr --- src/index.js | 1 + src/utils/AttributionManager.js | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index 691e3efc0..ad41361bb 100644 --- a/src/index.js +++ b/src/index.js @@ -116,6 +116,7 @@ export { AbstractAttributionManager, iterateStructsByIdSet, createAttributionManagerFromDiff, + DiffAttributionManager, createIdSet, mergeIdSets, cloneDoc diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 9073396aa..d8ab2f377 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -339,7 +339,6 @@ export class DiffAttributionManager extends ObservableV2 { if (content.getLength() === 0 || (content instanceof ContentDeleted && (s.attrs != null || shouldRender) && !this.inserts.has(client, s.clock))) { // @todo possibly remove this.inserts.. // Retrieved item is never more fragmented than the newer item. const prevItem = getItem(this._prevDocStore, createID(client, s.clock)) - const originalContentLen = content.getLength() content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content // trim itemContent to the correct size. const diffStart = s.clock - prevItem.id.clock @@ -348,9 +347,7 @@ export class DiffAttributionManager extends ObservableV2 { } } const c = content - if (s.len < c.getLength()) { - content = c.splice(s.len) - } + content = c.splice(s.len) if (shouldRender || !deleted || s.attrs != null) { contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) } From 6d7ccf66ed56c24b928b99f79ad9b6f89e580207 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 1 Jun 2025 01:09:52 +0200 Subject: [PATCH 322/362] fix am.readContent --- src/utils/AttributionManager.js | 51 +++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index d8ab2f377..5e67a12fa 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -17,7 +17,8 @@ import { applyUpdate, writeIdSet, UpdateEncoderV1, - transact + transact, + createMaybeAttrRange } from '../internals.js' import * as error from 'lib0/error' @@ -329,29 +330,41 @@ export class DiffAttributionManager extends ObservableV2 { * @param {number} client * @param {number} clock * @param {boolean} deleted - * @param {AbstractContent} content + * @param {AbstractContent} _content * @param {0|1|2} shouldRender - whether this should render or just result in a `retain` operation */ - readContent (contents, client, clock, deleted, content, shouldRender) { - const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, content.getLength()) - content = slice.length === 1 ? content : content.copy() - slice.forEach(s => { - if (content.getLength() === 0 || (content instanceof ContentDeleted && (s.attrs != null || shouldRender) && !this.inserts.has(client, s.clock))) { // @todo possibly remove this.inserts.. + readContent (contents, client, clock, deleted, _content, shouldRender) { + const slice = (deleted ? this.deletes : this.inserts).slice(client, clock, _content.getLength()) + /** + * @type {AbstractContent?} + */ + let content = slice.length === 1 ? _content : _content.copy() + for (let i = 0; i < slice.length; i++) { + const s = slice[i] + if (content == null || content instanceof ContentDeleted) { + if ((!shouldRender && s.attrs == null) || this.inserts.has(client, s.clock)) { + continue + } // Retrieved item is never more fragmented than the newer item. const prevItem = getItem(this._prevDocStore, createID(client, s.clock)) + const diffStart = s.clock - prevItem.id.clock content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content // trim itemContent to the correct size. - const diffStart = s.clock - prevItem.id.clock if (diffStart > 0) { content = content.splice(diffStart) } } - const c = content - content = c.splice(s.len) + const c = /** @type {AbstractContent} */ (content) + const clen = c.getLength() + if (clen < s.len) { + slice.splice(i + 1, 0, createMaybeAttrRange(s.clock + clen, s.len - clen, s.attrs)) + s.len = clen + } + content = s.len < clen ? c.splice(s.len) : null if (shouldRender || !deleted || s.attrs != null) { contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) } - }) + } } /** @@ -362,16 +375,12 @@ export class DiffAttributionManager extends ObservableV2 { if (!item.deleted) { return item.content.isCountable() ? item.length : 0 } - const slice = this.deletes.sliceId(item.id, item.length) - let content = item.content - if (content instanceof ContentDeleted && slice[0].attrs != null && !this.inserts.hasId(item.id)) { - const prevItem = getItem(this._prevDocStore, item.id) - content = prevItem.content - } - if (!content.isCountable()) { - return 0 - } - return slice.reduce((len, s) => s.attrs != null ? len + s.len : len, 0) + /** + * @type {Array>} + */ + const cs = [] + this.readContent(cs, item.id.client, item.id.clock, true, item.content, 0) + return cs.reduce((cnt, c) => cnt + ((c.attrs != null && c.content.isCountable()) ? c.content.getLength() : 0), 0) } } From 4d508918e961c8aa0510c9f05e71527e028fe592 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 2 Jun 2025 00:57:14 +0200 Subject: [PATCH 323/362] fuzz tests for unformatted text run --- src/types/YText.js | 54 +++++++++++++++++++++++++-------- src/utils/AttributionManager.js | 16 +++++----- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index ace4c6c58..d963c0b03 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -34,6 +34,7 @@ import { import * as delta from '../utils/Delta.js' +import * as math from 'lib0/math' import * as traits from 'lib0/traits' import * as object from 'lib0/object' import * as map from 'lib0/map' @@ -105,7 +106,7 @@ export class ItemTextListPosition { (length > 0 || ( negatedAttributes.size > 0 && - (this.right.deleted || this.right.content.constructor === ContentFormat) + ((this.right.deleted && this.am.contentLength(this.right) === 0) || this.right.content.constructor === ContentFormat) ) ) ) { @@ -133,11 +134,28 @@ export class ItemTextListPosition { break } default: { - const rightLen = this.am.contentLength(this.right) + const item = this.right + const rightLen = this.am.contentLength(item) if (length < rightLen) { - getItemCleanStart(transaction, createID(this.right.id.client, this.right.id.clock + length)) + /** + * @type {Array>} + */ + const contents = [] + this.am.readContent(contents, item.id.client, item.id.clock, item.deleted, item.content, 0) + let i = 0 + for (; i < contents.length && length > 0; i++) { + const c = contents[i] + if ((!c.deleted || c.attrs != null) && c.content.isCountable()) { + length -= c.content.getLength() + } + } + if (length < 0 || (length === 0 && i !== contents.length)) { + const c = contents[--i] + getItemCleanStart(transaction, createID(item.id.client, c.clock + c.content.getLength() + length)) + } + } else { + length -= rightLen } - length -= rightLen break } } @@ -554,16 +572,26 @@ const deleteText = (transaction, currPos, length) => { } } else if (currPos.am !== noAttributionsManager) { const item = currPos.right - let d = currPos.am.contentLength(item) - if (d > 0) { - if (length < d) { - getItemCleanStart(transaction, createID(currPos.right.id.client, currPos.right.id.clock + length)) - d = length + /** + * @type {Array>} + */ + const contents = [] + currPos.am.readContent(contents, item.id.client, item.id.clock, true, item.content, 0) + for (let i = 0; i < contents.length; i++) { + const c = contents[i] + if (c.content.isCountable() && c.attrs != null) { + // deleting already deleted content. store that information in a meta property, but do + // nothing + const contentLen = math.min(c.content.getLength(), length) + map.setIfUndefined(transaction.meta, 'attributedDeletes', createIdSet).add(item.id.client, c.clock, contentLen) + length -= contentLen } - // deleting already deleted content. store that information in a meta property, but do - // nothing - map.setIfUndefined(transaction.meta, 'attributedDeletes', createIdSet).add(item.id.client, item.id.clock, d) - length -= d + } + const lastContent = contents.length > 0 ? contents[contents.length - 1] : null + const nextItemClock = item.id.clock + item.length + const nextContentClock = lastContent != null ? lastContent.clock + lastContent.content.getLength() : nextItemClock + if (nextContentClock < nextItemClock) { + getItemCleanStart(transaction, createID(item.id.client, nextContentClock)) } } currPos.forward() diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 5e67a12fa..2c4eaeb6e 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -89,12 +89,14 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { export class AttributedContent { /** * @param {AbstractContent} content + * @param {number} clock * @param {boolean} deleted * @param {Array> | null} attrs * @param {0|1|2} renderBehavior */ - constructor (content, deleted, attrs, renderBehavior) { + constructor (content, clock, deleted, attrs, renderBehavior) { this.content = content + this.clock = clock this.deleted = deleted this.attrs = attrs this.render = renderBehavior === 0 ? false : (renderBehavior === 1 ? (!deleted || attrs != null) : true) @@ -169,7 +171,7 @@ export class TwosetAttributionManager extends ObservableV2 { content = c.splice(s.len) } if (!deleted || s.attrs != null || shouldRender) { - contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) + contents.push(new AttributedContent(c, s.clock, deleted, s.attrs, shouldRender)) } }) } @@ -200,14 +202,14 @@ export class NoAttributionsManager extends ObservableV2 { /** * @param {Array>} contents - where to write the result * @param {number} _client - * @param {number} _clock + * @param {number} clock * @param {boolean} deleted * @param {AbstractContent} content * @param {0|1|2} shouldRender - whether this should render or just result in a `retain` operation */ - readContent (contents, _client, _clock, deleted, content, shouldRender) { + readContent (contents, _client, clock, deleted, content, shouldRender) { if (!deleted || shouldRender) { - contents.push(new AttributedContent(content, deleted, null, shouldRender)) + contents.push(new AttributedContent(content, clock, deleted, null, shouldRender)) } } @@ -362,7 +364,7 @@ export class DiffAttributionManager extends ObservableV2 { } content = s.len < clen ? c.splice(s.len) : null if (shouldRender || !deleted || s.attrs != null) { - contents.push(new AttributedContent(c, deleted, s.attrs, shouldRender)) + contents.push(new AttributedContent(c, s.clock, deleted, s.attrs, shouldRender)) } } } @@ -444,7 +446,7 @@ export class SnapshotAttributionManager extends ObservableV2 { if (s.attrs?.length === 0) { attrsWithoutChange = null } - contents.push(new AttributedContent(c, deleted, attrsWithoutChange, shouldRender)) + contents.push(new AttributedContent(c, s.clock, deleted, attrsWithoutChange, shouldRender)) } }) } From c37ee3ee8cc55673162077e6eeaa967d20e90e40 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 2 Jun 2025 14:12:43 +0200 Subject: [PATCH 324/362] fix suggestion issues with formatting by introducing an option to disable automattic formatting cleanups --- src/types/YText.js | 5 +++++ src/utils/Doc.js | 12 +++++++++--- src/utils/Transaction.js | 7 ++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index d963c0b03..fd2660bf5 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -387,6 +387,7 @@ const insertText = (transaction, parent, currPos, text, attributes) => { * @function */ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAttributes) => { + if (!transaction.doc.cleanupFormatting) return 0 /** * @type {Item|null} */ @@ -417,6 +418,7 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt if (endFormats.get(key) !== content || startAttrValue === value) { // Either this format is overwritten or it is not necessary because the attribute already existed. start.delete(transaction) + transaction.cleanUps.add(start.id.client, start.id.clock, start.length) cleanups++ if (!reachedCurr && (currAttributes.get(key) ?? null) === value && startAttrValue !== value) { if (startAttrValue === null) { @@ -443,6 +445,7 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt * @param {Item | null} item */ const cleanupContextlessFormattingGap = (transaction, item) => { + if (!transaction.doc.cleanupFormatting) return 0 // iterate until item.right is null or content while (item && item.right && (item.right.deleted || !item.right.countable)) { item = item.right @@ -454,6 +457,7 @@ const cleanupContextlessFormattingGap = (transaction, item) => { const key = /** @type {ContentFormat} */ (item.content).key if (attrs.has(key)) { item.delete(transaction) + transaction.cleanUps.add(item.id.client, item.id.clock, item.length) } else { attrs.add(key) } @@ -475,6 +479,7 @@ const cleanupContextlessFormattingGap = (transaction, item) => { * @return {number} How many formatting attributes have been cleaned up. */ export const cleanupYTextFormatting = type => { + if (!type.doc?.cleanupFormatting) return 0 let res = 0 transact(/** @type {Doc} */ (type.doc), transaction => { let start = /** @type {Item} */ (type._start) diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 83c65f3a7..958c22309 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -33,6 +33,9 @@ export const generateNewClientId = random.uint32 * @property {any} [DocOpts.meta] Any kind of meta information you want to associate with this document. If this is a subdocument, remote peers will store the meta information as well. * @property {boolean} [DocOpts.autoLoad] If a subdocument, automatically load document. If this is a subdocument, remote peers will load the document as well automatically. * @property {boolean} [DocOpts.shouldLoad] Whether the document should be synced by the provider now. This is toggled to true when you call ydoc.load() + * @property {boolean} [DocOpts.isSuggestionDoc] Set to true if this document merely suggests + * changes. If this flag is not set in a suggestion document, automatic formatting changes will be + * displayed as suggestions, which might not be intended. */ /** @@ -59,13 +62,15 @@ export class Doc extends ObservableV2 { /** * @param {DocOpts} opts configuration */ - constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true } = {}) { + constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true, isSuggestionDoc = true } = {}) { super() this.gc = gc this.gcFilter = gcFilter this.clientID = generateNewClientId() this.guid = guid this.collectionid = collectionid + this.isSuggestionDoc = isSuggestionDoc + this.cleanupFormatting = !isSuggestionDoc /** * @type {Map>>} */ @@ -350,9 +355,10 @@ export class Doc extends ObservableV2 { /** * @param {Doc} ydoc + * @param {DocOpts} [opts] */ -export const cloneDoc = ydoc => { - const clone = new Doc() +export const cloneDoc = (ydoc, opts) => { + const clone = new Doc(opts) applyUpdate(clone, encodeStateAsUpdate(ydoc)) return clone } diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 5f9354375..6f1acf387 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -62,6 +62,11 @@ export class Transaction { * Describes the set of deleted items by ids */ this.deleteSet = createIdSet() + /** + * Describes the set of items that are cleaned up / deleted by ids. It is a subset of + * this.deleteSet + */ + this.cleanUps = createIdSet() /** * Describes the set of inserted items by ids */ @@ -354,7 +359,7 @@ const cleanupTransactions = (transactionCleanups, i) => { }) fs.push(() => doc.emit('afterTransaction', [transaction, doc])) callAll(fs, []) - if (transaction._needFormattingCleanup) { + if (transaction._needFormattingCleanup && doc.cleanupFormatting) { cleanupYTextAfterTransaction(transaction) } } finally { From e62e1d7c5339af71fdb4e37169e1e5bcf2149b8b Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 5 Jun 2025 14:52:55 +0200 Subject: [PATCH 325/362] implement StructSet abstraction --- src/index.js | 2 +- src/internals.js | 5 +- src/structs/Item.js | 26 +++--- src/types/YText.js | 2 +- src/utils/AttributionManager.js | 10 +-- src/utils/Doc.js | 2 +- src/utils/IdSet.js | 72 ++++++++++------ src/utils/RelativePosition.js | 3 +- src/utils/StructSet.js | 136 ++++++++++++++++++++++++++++++ src/utils/StructStore.js | 11 ++- src/utils/encoding.js | 141 ++++++++------------------------ tests/attribution.tests.js | 2 +- tests/updates.tests.js | 4 +- tests/y-xml.tests.js | 6 +- 14 files changed, 257 insertions(+), 165 deletions(-) create mode 100644 src/utils/StructSet.js diff --git a/src/index.js b/src/index.js index ad41361bb..e4c811e2b 100644 --- a/src/index.js +++ b/src/index.js @@ -105,7 +105,7 @@ export { IdMap, createIdMap, createAttributionItem, - createInsertionSetFromStructStore, + createInsertSetFromStructStore as createInsertionSetFromStructStore, diffIdMap, diffIdSet, AttributionItem as Attribution, diff --git a/src/internals.js b/src/internals.js index 6741e0e03..6d98c8516 100644 --- a/src/internals.js +++ b/src/internals.js @@ -16,6 +16,9 @@ export * from './utils/Transaction.js' export * from './utils/UndoManager.js' export * from './utils/updates.js' export * from './utils/YEvent.js' +export * from './utils/StructSet.js' +export * from './utils/IdMap.js' +export * from './utils/AttributionManager.js' export * from './types/AbstractType.js' export * from './types/YArray.js' @@ -40,5 +43,3 @@ export * from './structs/ContentString.js' export * from './structs/ContentType.js' export * from './structs/Item.js' export * from './structs/Skip.js' -export * from './utils/IdMap.js' -export * from './utils/AttributionManager.js' diff --git a/src/structs/Item.js b/src/structs/Item.js index 838d6b118..d6cbfa4db 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -74,7 +74,7 @@ export const keepItem = (item, keep) => { /** * Split leftItem into two items - * @param {Transaction} transaction + * @param {Transaction?} transaction * @param {Item} leftItem * @param {number} diff * @return {Item} @@ -104,17 +104,19 @@ export const splitItem = (transaction, leftItem, diff) => { if (leftItem.redone !== null) { rightItem.redone = createID(leftItem.redone.client, leftItem.redone.clock + diff) } - // update left (do not set leftItem.rightOrigin as it will lead to problems when syncing) - leftItem.right = rightItem - // update right - if (rightItem.right !== null) { - rightItem.right.left = rightItem - } - // right is more specific. - transaction._mergeStructs.push(rightItem) - // update parent._map - if (rightItem.parentSub !== null && rightItem.right === null) { - /** @type {AbstractType} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem) + if (transaction != null) { + // update left (do not set leftItem.rightOrigin as it will lead to problems when syncing) + leftItem.right = rightItem + // update right + if (rightItem.right !== null) { + rightItem.right.left = rightItem + } + // right is more specific. + transaction._mergeStructs.push(rightItem) + // update parent._map + if (rightItem.parentSub !== null && rightItem.right === null) { + /** @type {AbstractType} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem) + } } leftItem.length = diff return rightItem diff --git a/src/types/YText.js b/src/types/YText.js index fd2660bf5..20dc66623 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -249,7 +249,7 @@ const insertNegatedAttributes = (transaction, parent, currPos, negatedAttributes // check if we really need to remove attributes while ( currPos.right !== null && ( - (currPos.right.deleted && (currPos.am == noAttributionsManager || currPos.am.contentLength(currPos.right) === 0)) || ( + (currPos.right.deleted && (currPos.am === noAttributionsManager || currPos.am.contentLength(currPos.right) === 0)) || ( currPos.right.content.constructor === ContentFormat && equalAttrs(negatedAttributes.get(/** @type {ContentFormat} */ (currPos.right.content).key), /** @type {ContentFormat} */ (currPos.right.content).value) ) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 2c4eaeb6e..b8ca4b02e 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -1,7 +1,7 @@ import { getItem, diffIdSet, - createInsertionSetFromStructStore, + createInsertSetFromStructStore, createDeleteSetFromStructStore, createIdMapFromIdSet, ContentDeleted, @@ -236,8 +236,8 @@ export class DiffAttributionManager extends ObservableV2 { */ constructor (prevDoc, nextDoc) { super() - const _nextDocInserts = createInsertionSetFromStructStore(nextDoc.store, false) // unmaintained - const _prevDocInserts = createInsertionSetFromStructStore(prevDoc.store, false) // unmaintained + const _nextDocInserts = createInsertSetFromStructStore(nextDoc.store, false) // unmaintained + const _prevDocInserts = createInsertSetFromStructStore(prevDoc.store, false) // unmaintained const nextDocDeletes = createDeleteSetFromStructStore(nextDoc.store) // maintained const prevDocDeletes = createDeleteSetFromStructStore(prevDoc.store) // maintained this.inserts = createIdMapFromIdSet(diffIdSet(_nextDocInserts, _prevDocInserts), []) @@ -291,7 +291,7 @@ export class DiffAttributionManager extends ObservableV2 { this._afterTrListener = nextDoc.on('afterTransaction', (tr) => { // apply deletes on attributed deletes (content that is already deleted, but is rendered by // the attribution manager) - if (!this.suggestionMode && tr.local && (this.suggestionOrigins == null || this.suggestionOrigins.some(o => o === origin))) { + if (!this.suggestionMode && tr.local && (this.suggestionOrigins == null || this.suggestionOrigins.some(o => o === tr.origin))) { const attributedDeletes = tr.meta.get('attributedDeletes') if (attributedDeletes != null) { transact(prevDoc, () => { @@ -456,7 +456,7 @@ export class SnapshotAttributionManager extends ObservableV2 { * @return {number} */ contentLength (item) { - return item.content.isCountable() + return item.content.isCountable() ? (item.deleted ? this.attrs.sliceId(item.id, item.length).reduce((len, s) => s.attrs != null ? len + s.len : len, 0) : item.length diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 958c22309..3381e250c 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -62,7 +62,7 @@ export class Doc extends ObservableV2 { /** * @param {DocOpts} opts configuration */ - constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true, isSuggestionDoc = true } = {}) { + constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true, isSuggestionDoc = false} = {}) { super() this.gc = gc this.gcFilter = gcFilter diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index 097cb1544..f9a5160ea 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -79,12 +79,18 @@ export class MaybeIdRange { */ export const createMaybeIdRange = (clock, len, exists) => new MaybeIdRange(clock, len, exists) -class IdRanges { +export class IdRanges { /** * @param {Array} ids */ constructor (ids) { this.sorted = false + /** + * A typical use-case for IdSet is to append data. We heavily optimize this case by allowing the + * last item to be mutated ef it isn't used currently. + * This flag is true if the last item was exposed to the outside. + */ + this._lastIsUsed = false /** * @private */ @@ -102,7 +108,12 @@ class IdRanges { add (clock, length) { const last = this._ids[this._ids.length - 1] if (last.clock + last.len === clock) { - this._ids[this._ids.length - 1] = new IdRange(last.clock, last.len + length) + if (this._lastIsUsed) { + this._ids[this._ids.length - 1] = new IdRange(last.clock, last.len + length) + this._lastIsUsed = false + } else { + this._ids[this._ids.length - 1].len += length + } } else { this.sorted = false this._ids.push(new IdRange(clock, length)) @@ -110,10 +121,11 @@ class IdRanges { } /** - * Return the list of id ranges, sorted and merged. + * Return the list of immutable id ranges, sorted and merged. */ getIds () { const ids = this._ids + this._lastIsUsed = true if (!this.sorted) { this.sorted = true ids.sort((a, b) => a.clock - b.clock) @@ -153,6 +165,10 @@ export class IdSet { this.clients = new Map() } + isEmpty () { + return this.clients.size === 0 + } + /** * @param {(idrange:IdRange, client:number) => void} f */ @@ -606,31 +622,41 @@ export const createDeleteSetFromStructStore = ss => { } /** - * @param {import('../internals.js').StructStore} ss + * @param {Array} structs * @param {boolean} filterDeleted + * */ -export const createInsertionSetFromStructStore = (ss, filterDeleted) => { - const idset = createIdSet() - ss.clients.forEach((structs, client) => { - /** - * @type {Array} - */ - const iditems = [] - for (let i = 0; i < structs.length; i++) { - const struct = structs[i] - if (!(filterDeleted && struct.deleted)) { - const clock = struct.id.clock - let len = struct.length - if (i + 1 < structs.length) { - // eslint-disable-next-line - for (let next = structs[i + 1]; i + 1 < structs.length && !(filterDeleted && next.deleted); next = structs[++i + 1]) { - len += next.length - } +export const _createInsertSliceFromStructs = (structs, filterDeleted) => { + /** + * @type {Array} + */ + const iditems = [] + for (let i = 0; i < structs.length; i++) { + const struct = structs[i] + if (!(filterDeleted && struct.deleted)) { + const clock = struct.id.clock + let len = struct.length + if (i + 1 < structs.length) { + // eslint-disable-next-line + for (let next = structs[i + 1]; i + 1 < structs.length && !(filterDeleted && next.deleted); next = structs[++i + 1]) { + len += next.length } - iditems.push(new IdRange(clock, len)) } + iditems.push(new IdRange(clock, len)) } - if (iditems.length > 0) { + } + return iditems +} + +/** + * @param {import('../internals.js').StructStore} ss + * @param {boolean} filterDeleted + */ +export const createInsertSetFromStructStore = (ss, filterDeleted) => { + const idset = createIdSet() + ss.clients.forEach((structs, client) => { + const iditems = _createInsertSliceFromStructs(structs, filterDeleted) + if (iditems.length !== 0) { idset.clients.set(client, new IdRanges(iditems)) } }) diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index 3dd43ccde..a3ec3fdfe 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -9,8 +9,7 @@ import { ContentType, followRedone, getItem, - StructStore, ID, Doc, AbstractType, - noAttributionsManager, // eslint-disable-line + StructStore, ID, Doc, AbstractType, noAttributionsManager, // eslint-disable-line } from '../internals.js' import * as encoding from 'lib0/encoding' diff --git a/src/utils/StructSet.js b/src/utils/StructSet.js new file mode 100644 index 000000000..779068ad5 --- /dev/null +++ b/src/utils/StructSet.js @@ -0,0 +1,136 @@ +import { + createID, + readItemContent, + findIndexCleanStart, + Skip, + UpdateDecoderV1, UpdateDecoderV2, IdSet, Doc, GC, Item, ID, // eslint-disable-line +} from '../internals.js' + +import * as decoding from 'lib0/decoding' +import * as binary from 'lib0/binary' +import * as map from 'lib0/map' + +/** + * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder The decoder object to read data from. + * @param {Doc} doc + * @return {StructSet} + * + * @private + * @function + */ +export const readStructSet = (decoder, doc) => { + const clientRefs = new StructSet() + const numOfStateUpdates = decoding.readVarUint(decoder.restDecoder) + for (let i = 0; i < numOfStateUpdates; i++) { + const numberOfStructs = decoding.readVarUint(decoder.restDecoder) + /** + * @type {Array} + */ + const refs = new Array(numberOfStructs) + const client = decoder.readClient() + let clock = decoding.readVarUint(decoder.restDecoder) + clientRefs.clients.set(client, new StructRange(refs)) + for (let i = 0; i < numberOfStructs; i++) { + const info = decoder.readInfo() + switch (binary.BITS5 & info) { + case 0: { // GC + const len = decoder.readLen() + refs[i] = new GC(createID(client, clock), len) + clock += len + break + } + case 10: { // Skip Struct (nothing to apply) + // @todo we could reduce the amount of checks by adding Skip struct to clientRefs so we know that something is missing. + const len = decoding.readVarUint(decoder.restDecoder) + refs[i] = new Skip(createID(client, clock), len) + clock += len + break + } + default: { // Item with content + /** + * The optimized implementation doesn't use any variables because inlining variables is faster. + * Below a non-optimized version is shown that implements the basic algorithm with + * a few comments + */ + const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0 + // If parent = null and neither left nor right are defined, then we know that `parent` is child of `y` + // and we read the next string as parentYKey. + // It indicates how we store/retrieve parent from `y.share` + // @type {string|null} + const struct = new Item( + createID(client, clock), + null, // left + (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null, // origin + null, // right + (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin + cantCopyParentInfo ? (decoder.readParentInfo() ? doc.get(decoder.readString()) : decoder.readLeftID()) : null, // parent + cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub + readItemContent(decoder, info) // item content + ) + refs[i] = struct + clock += struct.length + } + } + } + } + return clientRefs +} + +/** + * Remove item-ranges from the StructSet. + * + * @param {StructSet} ss + * @param {IdSet} exclude + */ +export const removeRangesFromStructSet = (ss, exclude) => { + exclude.clients.forEach((range, client) => { + const structs = /** @type {StructRange} */ (ss.clients.get(client))?.refs + if (structs != null) { + const firstStruct = structs[0] + const lastStruct = structs[structs.length - 1] + const idranges = range.getIds() + for (let i = 0; i < idranges.length; i++) { + const range = idranges[i] + let startIndex = 0 + let endIndex = structs.length + if (range.clock >= lastStruct.id.clock + lastStruct.length) continue + if (range.clock > firstStruct.id.clock) { + startIndex = findIndexCleanStart(null, structs, range.clock) + } + if (range.clock + range.len <= firstStruct.id.clock) continue + if (range.clock + range.len < lastStruct.id.clock + lastStruct.length) { + endIndex = findIndexCleanStart(null, structs, range.clock + range.len) + } + if (startIndex < endIndex) { + structs[startIndex] = new Skip(new ID(client, range.clock), range.len) + const d = endIndex - startIndex + if (d > 1) { + structs.splice(startIndex, d) + } + } + } + } + }) +} + +class StructRange { + /** + * @param {Array} refs + */ + constructor (refs) { + this.i = 0 + /** + * @type {Array} + */ + this.refs = refs + } +} + +export class StructSet { + constructor () { + /** + * @type {Map} + */ + this.clients = map.create() + } +} diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 72438dbb5..5ca7ba822 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -1,8 +1,9 @@ import { GC, splitItem, - Transaction, ID, Item, // eslint-disable-line - createDeleteSetFromStructStore + createDeleteSetFromStructStore, + createIdSet, + Transaction, ID, Item // eslint-disable-line } from '../internals.js' import * as math from 'lib0/math' @@ -23,6 +24,7 @@ export class StructStore { * @type {null | Uint8Array} */ this.pendingDs = null + this.skips = createIdSet() } get ds () { @@ -46,6 +48,9 @@ export const getStateVector = store => { const struct = structs[structs.length - 1] sm.set(client, struct.id.clock + struct.length) }) + store.skips.clients.forEach((range, client) => { + sm.set(client, range.getIds()[0].clock) + }) return sm } @@ -171,7 +176,7 @@ export const find = (store, id) => { export const getItem = /** @type {function(StructStore,ID):Item} */ (find) /** - * @param {Transaction} transaction + * @param {Transaction?} transaction * @param {Array} structs * @param {number} clock */ diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 621a58752..e4339e7c6 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -17,12 +17,10 @@ import { findIndexSS, getState, - createID, getStateVector, readAndApplyDeleteSet, writeIdSet, transact, - readItemContent, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, @@ -35,12 +33,14 @@ import { Skip, diffUpdateV2, convertUpdateFormatV2ToV1, - IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, createDeleteSetFromStructStore, // eslint-disable-line + readStructSet, + removeRangesFromStructSet, + createIdSet, + StructSet, IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore // eslint-disable-line } from '../internals.js' import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' -import * as binary from 'lib0/binary' import * as map from 'lib0/map' import * as math from 'lib0/math' import * as array from 'lib0/array' @@ -120,102 +120,6 @@ export const writeStructsFromIdSet = (encoder, store, idset) => { }) } -/** - * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder The decoder object to read data from. - * @param {Doc} doc - * @return {Map }>} - * - * @private - * @function - */ -export const readClientsStructRefs = (decoder, doc) => { - /** - * @type {Map }>} - */ - const clientRefs = map.create() - const numOfStateUpdates = decoding.readVarUint(decoder.restDecoder) - for (let i = 0; i < numOfStateUpdates; i++) { - const numberOfStructs = decoding.readVarUint(decoder.restDecoder) - /** - * @type {Array} - */ - const refs = new Array(numberOfStructs) - const client = decoder.readClient() - let clock = decoding.readVarUint(decoder.restDecoder) - // const start = performance.now() - clientRefs.set(client, { i: 0, refs }) - for (let i = 0; i < numberOfStructs; i++) { - const info = decoder.readInfo() - switch (binary.BITS5 & info) { - case 0: { // GC - const len = decoder.readLen() - refs[i] = new GC(createID(client, clock), len) - clock += len - break - } - case 10: { // Skip Struct (nothing to apply) - // @todo we could reduce the amount of checks by adding Skip struct to clientRefs so we know that something is missing. - const len = decoding.readVarUint(decoder.restDecoder) - refs[i] = new Skip(createID(client, clock), len) - clock += len - break - } - default: { // Item with content - /** - * The optimized implementation doesn't use any variables because inlining variables is faster. - * Below a non-optimized version is shown that implements the basic algorithm with - * a few comments - */ - const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0 - // If parent = null and neither left nor right are defined, then we know that `parent` is child of `y` - // and we read the next string as parentYKey. - // It indicates how we store/retrieve parent from `y.share` - // @type {string|null} - const struct = new Item( - createID(client, clock), - null, // left - (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null, // origin - null, // right - (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin - cantCopyParentInfo ? (decoder.readParentInfo() ? doc.get(decoder.readString()) : decoder.readLeftID()) : null, // parent - cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub - readItemContent(decoder, info) // item content - ) - /* A non-optimized implementation of the above algorithm: - - // The item that was originally to the left of this item. - const origin = (info & binary.BIT8) === binary.BIT8 ? decoder.readLeftID() : null - // The item that was originally to the right of this item. - const rightOrigin = (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null - const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0 - const hasParentYKey = cantCopyParentInfo ? decoder.readParentInfo() : false - // If parent = null and neither left nor right are defined, then we know that `parent` is child of `y` - // and we read the next string as parentYKey. - // It indicates how we store/retrieve parent from `y.share` - // @type {string|null} - const parentYKey = cantCopyParentInfo && hasParentYKey ? decoder.readString() : null - - const struct = new Item( - createID(client, clock), - null, // left - origin, // origin - null, // right - rightOrigin, // right origin - cantCopyParentInfo && !hasParentYKey ? decoder.readLeftID() : (parentYKey !== null ? doc.get(parentYKey) : null), // parent - cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub - readItemContent(decoder, info) // item content - ) - */ - refs[i] = struct - clock += struct.length - } - } - } - // console.log('time to read: ', performance.now() - start) // @todo remove - } - return clientRefs -} - /** * Resume computing structs generated by struct readers. * @@ -237,7 +141,7 @@ export const readClientsStructRefs = (decoder, doc) => { * * @param {Transaction} transaction * @param {StructStore} store - * @param {Map} clientsStructRefs + * @param {StructSet} clientsStructRefs * @return { null | { update: Uint8Array, missing: Map } } * * @private @@ -249,7 +153,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { */ const stack = [] // sort them so that we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user. - let clientsStructRefsIds = array.from(clientsStructRefs.keys()).sort((a, b) => a - b) + let clientsStructRefsIds = array.from(clientsStructRefs.clients.keys()).sort((a, b) => a - b) if (clientsStructRefsIds.length === 0) { return null } @@ -257,11 +161,11 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { if (clientsStructRefsIds.length === 0) { return null } - let nextStructsTarget = /** @type {{i:number,refs:Array}} */ (clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1])) + let nextStructsTarget = /** @type {{i:number,refs:Array}} */ (clientsStructRefs.clients.get(clientsStructRefsIds[clientsStructRefsIds.length - 1])) while (nextStructsTarget.refs.length === nextStructsTarget.i) { clientsStructRefsIds.pop() if (clientsStructRefsIds.length > 0) { - nextStructsTarget = /** @type {{i:number,refs:Array}} */ (clientsStructRefs.get(clientsStructRefsIds[clientsStructRefsIds.length - 1])) + nextStructsTarget = /** @type {{i:number,refs:Array}} */ (clientsStructRefs.clients.get(clientsStructRefsIds[clientsStructRefsIds.length - 1])) } else { return null } @@ -295,15 +199,21 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { // caching the state because it is used very often const state = new Map() + // // caching the state because it is used very often + // const currentInsertSet = createIdSet() + // clientsStructRefsIds.forEach(clientId => { + // currentInsertSet.clients.set(clientid, new IdRanges(_createInsertSliceFromStructs(store.clients.get(clientId) ?? [], false))) + // }) + const addStackToRestSS = () => { for (const item of stack) { const client = item.id.client - const inapplicableItems = clientsStructRefs.get(client) + const inapplicableItems = clientsStructRefs.clients.get(client) if (inapplicableItems) { // decrement because we weren't able to apply previous operation inapplicableItems.i-- restStructs.clients.set(client, inapplicableItems.refs.slice(inapplicableItems.i)) - clientsStructRefs.delete(client) + clientsStructRefs.clients.delete(client) inapplicableItems.i = 0 inapplicableItems.refs = [] } else { @@ -335,7 +245,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { /** * @type {{ refs: Array, i: number }} */ - const structRefs = clientsStructRefs.get(/** @type {number} */ (missing)) || { refs: [], i: 0 } + const structRefs = clientsStructRefs.clients.get(/** @type {number} */ (missing)) || { refs: [], i: 0 } if (structRefs.refs.length === structRefs.i) { // This update message causally depends on another update message that doesn't exist yet updateMissingSv(/** @type {number} */ (missing), getState(store, missing)) @@ -346,7 +256,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { } } else if (offset === 0 || offset < stackHead.length) { // all fine, apply the stackhead - stackHead.integrate(transaction, offset) + stackHead.integrate(transaction, offset) // since I'm splitting structs before integrating them, offset is no longer necessary state.set(stackHead.id.client, stackHead.id.clock + stackHead.length) } } @@ -406,7 +316,20 @@ export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = n const doc = transaction.doc const store = doc.store // let start = performance.now() - const ss = readClientsStructRefs(structDecoder, doc) + const ss = readStructSet(structDecoder, doc) + const knownState = createIdSet() + ss.clients.forEach((_, client) => { + const storeStructs = store.clients.get(client) + if (storeStructs) { + knownState.add(client, 0, storeStructs.length) + // remove known items from ss + store.skips.clients.get(client)?.getIds().forEach(idrange => { + knownState.delete(client, idrange.clock, idrange.len) + }) + } + }) + // remove known items from ss + removeRangesFromStructSet(ss, knownState) // console.log('time to read structs: ', performance.now() - start) // @todo remove // start = performance.now() // console.log('time to merge: ', performance.now() - start) // @todo remove diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js index 71ee0c1ca..29e21e4d0 100644 --- a/tests/attribution.tests.js +++ b/tests/attribution.tests.js @@ -18,7 +18,7 @@ export const testRelativePositions = _tc => { ytext.insert(0, 'hello world') const v1 = Y.cloneDoc(ydoc) ytext.delete(1, 6) - ytext.insert(1, 'x', ) + ytext.insert(1, 'x') const am = Y.createAttributionManagerFromDiff(v1, ydoc) const rel = Y.createRelativePositionFromTypeIndex(ytext, 9, 1, am) // pos after "hello wo" const abs1 = Y.createAbsolutePositionFromRelativePosition(rel, ydoc, true, am) diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 08468365b..1cb840864 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -1,7 +1,7 @@ import * as t from 'lib0/testing' import * as Y from '../src/index.js' import { init, compare } from './testHelper.js' // eslint-disable-line -import { readClientsStructRefs, readIdSet, UpdateDecoderV2, UpdateEncoderV2, writeIdSet } from '../src/internals.js' +import { readStructSet, readIdSet, UpdateDecoderV2, UpdateEncoderV2, writeIdSet } from '../src/internals.js' import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' import * as object from 'lib0/object' @@ -192,7 +192,7 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { // So we add all deletes from `diffed` to `partDeletes` and compare then const decoder = decoding.createDecoder(diffed) const updateDecoder = new UpdateDecoderV2(decoder) - readClientsStructRefs(updateDecoder, new Y.Doc()) + readStructSet(updateDecoder, new Y.Doc()) const ds = readIdSet(updateDecoder) const updateEncoder = new UpdateEncoderV2() encoding.writeVarUint(updateEncoder.restEncoder, 0) // 0 structs diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 48fe8f37f..44a4eb845 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -364,14 +364,14 @@ export const testElementAttributedContentViaDiffer = _tc => { t.group('test getContentDeep both docs synced', () => { t.info('expecting diffingAttributionManager to auto update itself') const expectedContent = delta.createArrayDelta().insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]).insert([ - delta.createTextDelta().insert('bigworld', null, { acceptInsert: ['unknown'] }) - ], null, { acceptInsert: ['unknown'] }) + delta.createTextDelta().insert('bigworld') + ]) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { acceptInsert: ['unknown'] } } }) + t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: null } }) t.assert(attributedContent.nodeName === 'UNDEFINED') }) } From f41e633041c34c012af349b6291dce7d51e04b74 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 6 Jun 2025 00:59:06 +0200 Subject: [PATCH 326/362] work on allowing skips in struct store --- src/structs/AbstractStruct.js | 7 +++++ src/structs/GC.js | 14 +++++++-- src/structs/Item.js | 17 +++++++---- src/structs/Skip.js | 32 +++++++++++++++----- src/utils/IdSet.js | 22 ++++++++------ src/utils/StructSet.js | 2 +- src/utils/StructStore.js | 23 ++++++++++++--- src/utils/Transaction.js | 3 +- src/utils/encoding.js | 55 +++++++++++++++++------------------ tests/testHelper.js | 6 ---- tests/updates.tests.js | 8 ++--- tests/y-array.tests.js | 2 +- 12 files changed, 122 insertions(+), 69 deletions(-) diff --git a/src/structs/AbstractStruct.js b/src/structs/AbstractStruct.js index 52773eb70..c0889f8e3 100644 --- a/src/structs/AbstractStruct.js +++ b/src/structs/AbstractStruct.js @@ -48,4 +48,11 @@ export class AbstractStruct { integrate (transaction, offset) { throw error.methodUnimplemented() } + + /** + * @param {number} diff + */ + splice (diff) { + throw error.methodUnimplemented() + } } diff --git a/src/structs/GC.js b/src/structs/GC.js index 710751cb5..788cb5ec1 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -3,7 +3,8 @@ import { addStruct, addStructToIdSet, addToIdSet, - UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction // eslint-disable-line + UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, // eslint-disable-line + createID } from '../internals.js' export const structGCRefNumber = 0 @@ -32,7 +33,7 @@ export class GC extends AbstractStruct { /** * @param {Transaction} transaction - * @param {number} offset + * @param {number} offset - @todo remove offset parameter */ integrate (transaction, offset) { if (offset > 0) { @@ -61,4 +62,13 @@ export class GC extends AbstractStruct { getMissing (transaction, store) { return null } + + /** + * @param {number} diff + */ + splice (diff) { + const other = new GC(createID(this.id.client, this.id.clock + diff), this.length - diff) + this.length = diff + return other + } } diff --git a/src/structs/Item.js b/src/structs/Item.js index d6cbfa4db..c0b281ec0 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -22,6 +22,7 @@ import { readContentType, addChangedTypeToTransaction, addStructToIdSet, + Skip, IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line } from '../internals.js' @@ -117,6 +118,9 @@ export const splitItem = (transaction, leftItem, diff) => { if (rightItem.parentSub !== null && rightItem.right === null) { /** @type {AbstractType} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem) } + } else { + rightItem.left = null + rightItem.right = null } leftItem.length = diff return rightItem @@ -368,18 +372,16 @@ export class Item extends AbstractStruct { * @return {null | number} */ getMissing (transaction, store) { - if (this.origin && this.origin.client !== this.id.client && this.origin.clock >= getState(store, this.origin.client)) { + if (this.origin && (this.origin.clock >= getState(store, this.origin.client) || store.skips.hasId(this.origin))) { return this.origin.client } - if (this.rightOrigin && this.rightOrigin.client !== this.id.client && this.rightOrigin.clock >= getState(store, this.rightOrigin.client)) { + if (this.rightOrigin && (this.rightOrigin.clock >= getState(store, this.rightOrigin.client) || store.skips.hasId(this.rightOrigin))) { return this.rightOrigin.client } - if (this.parent && this.parent.constructor === ID && this.id.client !== this.parent.client && this.parent.clock >= getState(store, this.parent.client)) { + if (this.parent && this.parent.constructor === ID && (this.parent.clock >= getState(store, this.parent.client) || store.skips.hasId(this.parent))) { return this.parent.client } - // We have all missing ids, now find the items - if (this.origin) { this.left = getItemCleanEnd(transaction, store, this.origin) this.origin = this.left.lastId @@ -407,6 +409,11 @@ export class Item extends AbstractStruct { this.parent = /** @type {ContentType} */ (parentItem.content).type } } + // @todo remove thgis + if (this.left instanceof Skip || this.right instanceof Skip || this.parent instanceof Skip) { + debugger + throw new Error('dtruinae') + } return null } diff --git a/src/structs/Skip.js b/src/structs/Skip.js index 3f7caafa3..1fa81a58c 100644 --- a/src/structs/Skip.js +++ b/src/structs/Skip.js @@ -1,8 +1,11 @@ import { AbstractStruct, - UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID // eslint-disable-line + addStruct, + addToIdSet, + UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, // eslint-disable-line + createID } from '../internals.js' -import * as error from 'lib0/error' + import * as encoding from 'lib0/encoding' export const structSkipRefNumber = 10 @@ -12,7 +15,7 @@ export const structSkipRefNumber = 10 */ export class Skip extends AbstractStruct { get deleted () { - return true + return false } delete () {} @@ -34,8 +37,12 @@ export class Skip extends AbstractStruct { * @param {number} offset */ integrate (transaction, offset) { - // skip structs cannot be integrated - error.unexpectedCase() + if (offset > 0) { + this.id.clock += offset + this.length -= offset + } + addToIdSet(transaction.doc.store.skips, this.id.client, this.id.clock, this.length) + addStruct(transaction.doc.store, this) } /** @@ -49,11 +56,20 @@ export class Skip extends AbstractStruct { } /** - * @param {Transaction} transaction - * @param {StructStore} store + * @param {Transaction} _transaction + * @param {StructStore} _store * @return {null | number} */ - getMissing (transaction, store) { + getMissing (_transaction, _store) { return null } + + /** + * @param {number} diff + */ + splice (diff) { + const other = new Skip(createID(this.id.client, this.id.clock + diff), this.length - diff) + this.length = diff + return other + } } diff --git a/src/utils/IdSet.js b/src/utils/IdSet.js index f9a5160ea..8b3a20deb 100644 --- a/src/utils/IdSet.js +++ b/src/utils/IdSet.js @@ -7,7 +7,7 @@ import { IdMap, AttrRanges, AttrRange, - AbstractStruct, DSDecoderV1, IdSetEncoderV1, DSDecoderV2, IdSetEncoderV2, Item, GC, StructStore, Transaction, ID, AttributionItem, // eslint-disable-line + Skip, AbstractStruct, DSDecoderV1, IdSetEncoderV1, DSDecoderV2, IdSetEncoderV2, Item, GC, StructStore, Transaction, ID, AttributionItem, // eslint-disable-line } from '../internals.js' import * as array from 'lib0/array' @@ -749,24 +749,28 @@ export const readAndApplyDeleteSet = (decoder, transaction, store) => { let index = findIndexSS(structs, clock) /** * We can ignore the case of GC and Delete structs, because we are going to skip them - * @type {Item} + * @type {Item | GC | Skip} */ - // @ts-ignore let struct = structs[index] // split the first item if necessary - if (!struct.deleted && struct.id.clock < clock) { - structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock)) - index++ // increase we now want to use the next struct + if (!struct.deleted && struct.id.clock < clock && struct instanceof Item) { + // increment index, we now want to use the next struct + structs.splice(++index, 0, splitItem(transaction, struct, clock - struct.id.clock)) } while (index < structs.length) { // @ts-ignore struct = structs[index++] if (struct.id.clock < clockEnd) { if (!struct.deleted) { - if (clockEnd < struct.id.clock + struct.length) { - structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock)) + if (struct instanceof Item) { + if (clockEnd < struct.id.clock + struct.length) { + structs.splice(index, 0, splitItem(transaction, struct, clockEnd - struct.id.clock)) + } + struct.delete(transaction) + } else { // is a Skip - add range to unappliedDS + const c = math.max(struct.id.clock, clock) + unappliedDS.add(client, c, math.min(struct.length, clockEnd - c)) } - struct.delete(transaction) } } else { break diff --git a/src/utils/StructSet.js b/src/utils/StructSet.js index 779068ad5..3d60a0c8b 100644 --- a/src/utils/StructSet.js +++ b/src/utils/StructSet.js @@ -105,7 +105,7 @@ export const removeRangesFromStructSet = (ss, exclude) => { structs[startIndex] = new Skip(new ID(client, range.clock), range.len) const d = endIndex - startIndex if (d > 1) { - structs.splice(startIndex, d) + structs.splice(startIndex + 1, d - 1) } } } diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 5ca7ba822..e2b3eca2a 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -3,7 +3,9 @@ import { splitItem, createDeleteSetFromStructStore, createIdSet, - Transaction, ID, Item // eslint-disable-line + Transaction, ID, Item, // eslint-disable-line + Skip, + createID } from '../internals.js' import * as math from 'lib0/math' @@ -104,7 +106,20 @@ export const addStruct = (store, struct) => { } else { const lastStruct = structs[structs.length - 1] if (lastStruct.id.clock + lastStruct.length !== struct.id.clock) { - throw error.unexpectedCase() + // this replaces an integrated skip + let index = findIndexSS(structs, struct.id.clock) + const skip = structs[index] + const diffStart = struct.id.clock - skip.id.clock + const diffEnd = skip.id.clock + skip.length - struct.id.clock - struct.length + if (diffStart > 0) { + structs.splice(index++, 0, new Skip(createID(struct.id.client, struct.id.clock), diffStart)) + } + if (diffEnd > 0) { + structs.splice(index + 1, 0, new Skip(createID(struct.id.client, struct.id.clock + struct.length), diffEnd)) + } + structs[index] = struct + store.skips.delete(struct.id.client, struct.id.clock, struct.length) + return } } structs.push(struct) @@ -183,8 +198,8 @@ export const getItem = /** @type {function(StructStore,ID):Item} */ (find) export const findIndexCleanStart = (transaction, structs, clock) => { const index = findIndexSS(structs, clock) const struct = structs[index] - if (struct.id.clock < clock && struct instanceof Item) { - structs.splice(index + 1, 0, splitItem(transaction, struct, clock - struct.id.clock)) + if (struct.id.clock < clock) { + structs.splice(index + 1, 0, struct instanceof Item ? splitItem(transaction, struct, clock - struct.id.clock) : struct.splice(clock - struct.id.clock)) return index + 1 } return index diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 6f1acf387..eab66829c 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -10,7 +10,8 @@ import { generateNewClientId, createID, cleanupYTextAfterTransaction, - IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, // eslint-disable-line + IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, + diffIdSet, // eslint-disable-line // insertIntoIdSet } from '../internals.js' diff --git a/src/utils/encoding.js b/src/utils/encoding.js index e4339e7c6..163cafabf 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -36,7 +36,8 @@ import { readStructSet, removeRangesFromStructSet, createIdSet, - StructSet, IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore // eslint-disable-line + StructSet, IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line + createID } from '../internals.js' import * as encoding from 'lib0/encoding' @@ -231,34 +232,31 @@ const integrateStructs = (transaction, store, clientsStructRefs) => { if (stackHead.constructor !== Skip) { const localClock = map.setIfUndefined(state, stackHead.id.client, () => getState(store, stackHead.id.client)) const offset = localClock - stackHead.id.clock - if (offset < 0) { - // update from the same client is missing + const missing = stackHead.getMissing(transaction, store) + if (missing !== null) { stack.push(stackHead) - updateMissingSv(stackHead.id.client, stackHead.id.clock - 1) - // hid a dead wall, add all items from stack to restSS - addStackToRestSS() + // get the struct reader that has the missing struct + /** + * @type {{ refs: Array, i: number }} + */ + const structRefs = clientsStructRefs.clients.get(/** @type {number} */ (missing)) || { refs: [], i: 0 } + if (structRefs.refs.length === structRefs.i || missing === stackHead.id.client || stack.some(s => s.id.client === missing)) { // @todo this could be optimized! + // This update message causally depends on another update message that doesn't exist yet + updateMissingSv(/** @type {number} */ (missing), getState(store, missing)) + addStackToRestSS() + } else { + stackHead = structRefs.refs[structRefs.i++] + continue + } } else { - const missing = stackHead.getMissing(transaction, store) - if (missing !== null) { - stack.push(stackHead) - // get the struct reader that has the missing struct - /** - * @type {{ refs: Array, i: number }} - */ - const structRefs = clientsStructRefs.clients.get(/** @type {number} */ (missing)) || { refs: [], i: 0 } - if (structRefs.refs.length === structRefs.i) { - // This update message causally depends on another update message that doesn't exist yet - updateMissingSv(/** @type {number} */ (missing), getState(store, missing)) - addStackToRestSS() - } else { - stackHead = structRefs.refs[structRefs.i++] - continue - } - } else if (offset === 0 || offset < stackHead.length) { - // all fine, apply the stackhead - stackHead.integrate(transaction, offset) // since I'm splitting structs before integrating them, offset is no longer necessary - state.set(stackHead.id.client, stackHead.id.clock + stackHead.length) + // all fine, apply the stackhead + // but first add a skip to structs if necessary + if (offset < 0) { + const skip = new Skip(createID(stackHead.id.client, localClock), -offset) + skip.integrate(transaction, 0) } + stackHead.integrate(transaction, 0) + state.set(stackHead.id.client, math.max(stackHead.id.clock + stackHead.length, localClock)) } } // iterate to next stackHead @@ -321,7 +319,8 @@ export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = n ss.clients.forEach((_, client) => { const storeStructs = store.clients.get(client) if (storeStructs) { - knownState.add(client, 0, storeStructs.length) + const last = storeStructs[storeStructs.length - 1] + knownState.add(client, 0, last.id.clock + last.length) // remove known items from ss store.skips.clients.get(client)?.getIds().forEach(idrange => { knownState.delete(client, idrange.clock, idrange.len) @@ -339,7 +338,7 @@ export const readUpdateV2 = (decoder, ydoc, transactionOrigin, structDecoder = n if (pending) { // check if we can apply something for (const [client, clock] of pending.missing) { - if (clock < getState(store, client)) { + if (ss.clients.has(client) || clock < getState(store, client)) { retry = true break } diff --git a/tests/testHelper.js b/tests/testHelper.js index ee55830cf..3a55a5533 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -97,12 +97,6 @@ export class TestYInstance extends Y.Doc { } this.updates.push(update) }) - this.on('afterTransaction', tr => { - // @ts-ignore - if (Array.from(tr.insertSet.clients.values()).some(ids => ids._ids.length !== 1)) { - throw new Error('Currently, we expect that idset contains exactly one item per client.') - } - }) this.connect() } diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 1cb840864..40ccc58a3 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -169,19 +169,19 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { // t.info('Target State: ') // enc.logUpdate(targetState) - cases.forEach((mergedUpdates) => { - // t.info('State Case $' + i + ':') + cases.forEach((mergedUpdates, i) => { + t.info(`State Case $${i} (${enc.description}):`) // enc.logUpdate(updates) const merged = new Y.Doc({ gc: false }) enc.applyUpdate(merged, mergedUpdates) t.compareArrays(merged.getArray().toArray(), ydoc.getArray().toArray()) t.compare(enc.encodeStateVector(merged), enc.encodeStateVectorFromUpdate(mergedUpdates)) - if (enc.updateEventName !== 'update') { // @todo should this also work on legacy updates? for (let j = 1; j < updates.length; j++) { const partMerged = enc.mergeUpdates(updates.slice(j)) const partMeta = enc.parseUpdateMeta(partMerged) - const targetSV = Y.encodeStateVectorFromUpdateV2(Y.mergeUpdatesV2(updates.slice(0, j))) + + const targetSV = enc.encodeStateVectorFromUpdate(enc.mergeUpdates(updates.slice(0, j))) const diffed = enc.diffUpdate(mergedUpdates, targetSV) const diffedMeta = enc.parseUpdateMeta(diffed) t.compare(partMeta, diffedMeta) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 4eaab5d2d..c2e9f7c75 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -604,7 +604,7 @@ const arrayTransactions = [ * @param {t.TestCase} tc */ export const testRepeatGeneratingYarrayTests6 = tc => { - applyRandomTests(tc, arrayTransactions, 6) + applyRandomTests(tc, arrayTransactions, 8) } /** From 78fb8d0b0a4bda195691134ddb26fb8c4e4f972d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 6 Jun 2025 02:09:24 +0200 Subject: [PATCH 327/362] fixed another splicing issue with skips --- src/utils/StructStore.js | 2 +- tests/y-array.tests.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index e2b3eca2a..27cc61260 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -112,7 +112,7 @@ export const addStruct = (store, struct) => { const diffStart = struct.id.clock - skip.id.clock const diffEnd = skip.id.clock + skip.length - struct.id.clock - struct.length if (diffStart > 0) { - structs.splice(index++, 0, new Skip(createID(struct.id.client, struct.id.clock), diffStart)) + structs.splice(index++, 0, new Skip(createID(struct.id.client, skip.id.clock), diffStart)) } if (diffEnd > 0) { structs.splice(index + 1, 0, new Skip(createID(struct.id.client, struct.id.clock + struct.length), diffEnd)) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index c2e9f7c75..8d9522577 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -604,7 +604,7 @@ const arrayTransactions = [ * @param {t.TestCase} tc */ export const testRepeatGeneratingYarrayTests6 = tc => { - applyRandomTests(tc, arrayTransactions, 8) + applyRandomTests(tc, arrayTransactions, 6*3) } /** From a76d6e1c0e86ea62317be65a1c14a8441772d88e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 6 Jun 2025 17:46:12 +0200 Subject: [PATCH 328/362] [skip allowed struct in yjs] fixed all tests --- src/utils/StructSet.js | 2 +- tests/y-array.tests.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/StructSet.js b/src/utils/StructSet.js index 3d60a0c8b..5f602810b 100644 --- a/src/utils/StructSet.js +++ b/src/utils/StructSet.js @@ -92,11 +92,11 @@ export const removeRangesFromStructSet = (ss, exclude) => { for (let i = 0; i < idranges.length; i++) { const range = idranges[i] let startIndex = 0 - let endIndex = structs.length if (range.clock >= lastStruct.id.clock + lastStruct.length) continue if (range.clock > firstStruct.id.clock) { startIndex = findIndexCleanStart(null, structs, range.clock) } + let endIndex = structs.length // must be set here, after structs is modified if (range.clock + range.len <= firstStruct.id.clock) continue if (range.clock + range.len < lastStruct.id.clock + lastStruct.length) { endIndex = findIndexCleanStart(null, structs, range.clock + range.len) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 8d9522577..4eaab5d2d 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -604,7 +604,7 @@ const arrayTransactions = [ * @param {t.TestCase} tc */ export const testRepeatGeneratingYarrayTests6 = tc => { - applyRandomTests(tc, arrayTransactions, 6*3) + applyRandomTests(tc, arrayTransactions, 6) } /** From 8ef8ffc250e8f0d3f2f9b03bbd6bb651e4b2f054 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 6 Jun 2025 19:08:31 +0200 Subject: [PATCH 329/362] fix gc splice issues happening in y-quill --- src/structs/ContentAny.js | 4 ++-- src/structs/ContentBinary.js | 4 ++-- src/structs/ContentDeleted.js | 8 ++++---- src/structs/ContentDoc.js | 4 ++-- src/structs/ContentEmbed.js | 4 ++-- src/structs/ContentFormat.js | 4 ++-- src/structs/ContentJSON.js | 4 ++-- src/structs/ContentString.js | 4 ++-- src/structs/ContentType.js | 12 ++++++------ src/structs/GC.js | 22 ++++++++++++---------- src/structs/Item.js | 12 ++++++------ src/utils/StructStore.js | 9 +++++---- src/utils/Transaction.js | 27 +++++++++++++-------------- 13 files changed, 60 insertions(+), 58 deletions(-) diff --git a/src/structs/ContentAny.js b/src/structs/ContentAny.js index 3ab2dc5ff..ba7a1b77a 100644 --- a/src/structs/ContentAny.js +++ b/src/structs/ContentAny.js @@ -76,9 +76,9 @@ export class ContentAny { */ delete (transaction) {} /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset diff --git a/src/structs/ContentBinary.js b/src/structs/ContentBinary.js index 2708f53b7..a376dbbca 100644 --- a/src/structs/ContentBinary.js +++ b/src/structs/ContentBinary.js @@ -66,9 +66,9 @@ export class ContentBinary { */ delete (transaction) {} /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset diff --git a/src/structs/ContentDeleted.js b/src/structs/ContentDeleted.js index dc9b776c2..102bcdfa8 100644 --- a/src/structs/ContentDeleted.js +++ b/src/structs/ContentDeleted.js @@ -68,13 +68,13 @@ export class ContentDeleted { } /** - * @param {Transaction} transaction + * @param {Transaction} _transaction */ - delete (transaction) {} + delete (_transaction) {} /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset diff --git a/src/structs/ContentDoc.js b/src/structs/ContentDoc.js index 15836f515..8491272b1 100644 --- a/src/structs/ContentDoc.js +++ b/src/structs/ContentDoc.js @@ -110,9 +110,9 @@ export class ContentDoc { } /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) { } + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder diff --git a/src/structs/ContentEmbed.js b/src/structs/ContentEmbed.js index 46fba375d..4cb2f9220 100644 --- a/src/structs/ContentEmbed.js +++ b/src/structs/ContentEmbed.js @@ -69,9 +69,9 @@ export class ContentEmbed { */ delete (transaction) {} /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset diff --git a/src/structs/ContentFormat.js b/src/structs/ContentFormat.js index 631e77559..d79aa8855 100644 --- a/src/structs/ContentFormat.js +++ b/src/structs/ContentFormat.js @@ -77,9 +77,9 @@ export class ContentFormat { */ delete (_transaction) {} /** - * @param {StructStore} _store + * @param {Transaction} _tr */ - gc (_store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} _offset diff --git a/src/structs/ContentJSON.js b/src/structs/ContentJSON.js index 29b2f7540..175917561 100644 --- a/src/structs/ContentJSON.js +++ b/src/structs/ContentJSON.js @@ -73,9 +73,9 @@ export class ContentJSON { */ delete (transaction) {} /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset diff --git a/src/structs/ContentString.js b/src/structs/ContentString.js index fa023ef95..a2f5f3684 100644 --- a/src/structs/ContentString.js +++ b/src/structs/ContentString.js @@ -84,9 +84,9 @@ export class ContentString { */ delete (transaction) {} /** - * @param {StructStore} store + * @param {Transaction} _tr */ - gc (store) {} + gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index 597c87190..13a09cd05 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -128,18 +128,18 @@ export class ContentType { } /** - * @param {StructStore} store + * @param {Transaction} tr */ - gc (store) { + gc (tr) { let item = this.type._start while (item !== null) { - item.gc(store, true) + item.gc(tr, true) item = item.right } this.type._start = null this.type._map.forEach(/** @param {Item | null} item */ (item) => { while (item !== null) { - item.gc(store, true) + item.gc(tr, true) item = item.left } }) @@ -148,9 +148,9 @@ export class ContentType { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder - * @param {number} offset + * @param {number} _offset */ - write (encoder, offset) { + write (encoder, _offset) { this.type._write(encoder) } diff --git a/src/structs/GC.js b/src/structs/GC.js index 788cb5ec1..8f1d2f9e4 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -3,8 +3,7 @@ import { addStruct, addStructToIdSet, addToIdSet, - UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, // eslint-disable-line - createID + UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction // eslint-disable-line } from '../internals.js' export const structGCRefNumber = 0 @@ -55,20 +54,23 @@ export class GC extends AbstractStruct { } /** - * @param {Transaction} transaction - * @param {StructStore} store + * @param {Transaction} _transaction + * @param {StructStore} _store * @return {null | number} */ - getMissing (transaction, store) { + getMissing (_transaction, _store) { return null } /** - * @param {number} diff + * gc structs can't be spliced. + * + * If this feature is required in the future, then need to try to merge this struct after + * transaction. + * + * @param {number} _diff */ - splice (diff) { - const other = new GC(createID(this.id.client, this.id.clock + diff), this.length - diff) - this.length = diff - return other + splice (_diff) { + return this } } diff --git a/src/structs/Item.js b/src/structs/Item.js index c0b281ec0..042c971c4 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -634,16 +634,16 @@ export class Item extends AbstractStruct { } /** - * @param {StructStore} store + * @param {Transaction} tr * @param {boolean} parentGCd */ - gc (store, parentGCd) { + gc (tr, parentGCd) { if (!this.deleted) { throw error.unexpectedCase() } - this.content.gc(store) + this.content.gc(tr) if (parentGCd) { - replaceStruct(store, this, new GC(this.id, this.length)) + replaceStruct(tr, this, new GC(this.id, this.length)) } else { this.content = new ContentDeleted(this.length) } @@ -799,9 +799,9 @@ export class AbstractContent { } /** - * @param {StructStore} _store + * @param {Transaction} _transaction */ - gc (_store) { + gc (_transaction) { throw error.methodUnimplemented() } diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 27cc61260..fdcd7a5a7 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -198,7 +198,7 @@ export const getItem = /** @type {function(StructStore,ID):Item} */ (find) export const findIndexCleanStart = (transaction, structs, clock) => { const index = findIndexSS(structs, clock) const struct = structs[index] - if (struct.id.clock < clock) { + if (struct.id.clock < clock && struct.constructor !== GC) { structs.splice(index + 1, 0, struct instanceof Item ? splitItem(transaction, struct, clock - struct.id.clock) : struct.splice(clock - struct.id.clock)) return index + 1 } @@ -247,16 +247,17 @@ export const getItemCleanEnd = (transaction, store, id) => { /** * Replace `item` with `newitem` in store - * @param {StructStore} store + * @param {Transaction} tr * @param {GC|Item} struct * @param {GC|Item} newStruct * * @private * @function */ -export const replaceStruct = (store, struct, newStruct) => { - const structs = /** @type {Array} */ (store.clients.get(struct.id.client)) +export const replaceStruct = (tr, struct, newStruct) => { + const structs = /** @type {Array} */ (tr.doc.store.clients.get(struct.id.client)) structs[findIndexSS(structs, struct.id.clock)] = newStruct + tr._mergeStructs.push(newStruct) } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index eab66829c..98caf9797 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -10,8 +10,7 @@ import { generateNewClientId, createID, cleanupYTextAfterTransaction, - IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, - diffIdSet, // eslint-disable-line + IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // insertIntoIdSet } from '../internals.js' @@ -239,14 +238,14 @@ const tryToMergeWithLefts = (structs, pos) => { } /** + * @param {Transaction} tr * @param {IdSet} ds - * @param {StructStore} store * @param {function(Item):boolean} gcFilter */ -const tryGcDeleteSet = (ds, store, gcFilter) => { +const tryGcDeleteSet = (tr, ds, gcFilter) => { for (const [client, _deleteItems] of ds.clients.entries()) { const deleteItems = _deleteItems.getIds() - const structs = /** @type {Array} */ (store.clients.get(client)) + const structs = /** @type {Array} */ (tr.doc.store.clients.get(client)) for (let di = deleteItems.length - 1; di >= 0; di--) { const deleteItem = deleteItems[di] const endDeleteItemClock = deleteItem.clock + deleteItem.len @@ -260,7 +259,7 @@ const tryGcDeleteSet = (ds, store, gcFilter) => { break } if (struct instanceof Item && struct.deleted && !struct.keep && gcFilter(struct)) { - struct.gc(store, false) + struct.gc(tr, false) } } } @@ -271,7 +270,7 @@ const tryGcDeleteSet = (ds, store, gcFilter) => { * @param {IdSet} ds * @param {StructStore} store */ -const tryMergeDeleteSet = (ds, store) => { +const tryMerge = (ds, store) => { // try to merge deleted / gc'd items // merge from right to left for better efficiency and so we don't miss any merge targets ds.clients.forEach((_deleteItems, client) => { @@ -293,13 +292,13 @@ const tryMergeDeleteSet = (ds, store) => { } /** - * @param {IdSet} ds - * @param {StructStore} store + * @param {Transaction} tr + * @param {IdSet} idset * @param {function(Item):boolean} gcFilter */ -export const tryGc = (ds, store, gcFilter) => { - tryGcDeleteSet(ds, store, gcFilter) - tryMergeDeleteSet(ds, store) +export const tryGc = (tr, idset, gcFilter) => { + tryGcDeleteSet(tr, idset, gcFilter) + tryMerge(idset, tr.doc.store) } /** @@ -367,9 +366,9 @@ const cleanupTransactions = (transactionCleanups, i) => { // Replace deleted items with ItemDeleted / GC. // This is where content is actually remove from the Yjs Doc. if (doc.gc) { - tryGcDeleteSet(ds, store, doc.gcFilter) + tryGcDeleteSet(transaction, ds, doc.gcFilter) } - tryMergeDeleteSet(ds, store) + tryMerge(ds, store) // on all affected store.clients props, try to merge transaction.insertSet.clients.forEach((ids, client) => { From 92bab00678f2aab683a7fdfcf746339b23b2e7e9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 7 Jun 2025 19:18:24 +0200 Subject: [PATCH 330/362] be able to encode partial state with holes correctly --- src/structs/AbstractStruct.js | 1 + src/structs/ContentAny.js | 9 ++--- src/structs/ContentBinary.js | 5 +-- src/structs/ContentDeleted.js | 5 +-- src/structs/ContentDoc.js | 5 +-- src/structs/ContentEmbed.js | 5 +-- src/structs/ContentFormat.js | 3 +- src/structs/ContentJSON.js | 9 ++--- src/structs/ContentString.js | 5 +-- src/structs/ContentType.js | 3 +- src/structs/GC.js | 14 +++++--- src/structs/Item.js | 36 +++++++++++++------ src/utils/Doc.js | 2 +- src/utils/Snapshot.js | 2 +- src/utils/StructSet.js | 1 + src/utils/StructStore.js | 7 ++-- src/utils/Transaction.js | 3 +- src/utils/encoding.js | 68 +++++++++++++++++++++++++++-------- src/utils/updates.js | 2 +- tests/updates.tests.js | 1 - 20 files changed, 127 insertions(+), 59 deletions(-) diff --git a/src/structs/AbstractStruct.js b/src/structs/AbstractStruct.js index c0889f8e3..9324a1b47 100644 --- a/src/structs/AbstractStruct.js +++ b/src/structs/AbstractStruct.js @@ -51,6 +51,7 @@ export class AbstractStruct { /** * @param {number} diff + * @return {import('../internals.js').GC|import('../internals.js').Item} */ splice (diff) { throw error.methodUnimplemented() diff --git a/src/structs/ContentAny.js b/src/structs/ContentAny.js index ba7a1b77a..4e32ac83b 100644 --- a/src/structs/ContentAny.js +++ b/src/structs/ContentAny.js @@ -82,11 +82,12 @@ export class ContentAny { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset + * @param {number} offsetEnd */ - write (encoder, offset) { - const len = this.arr.length - encoder.writeLen(len - offset) - for (let i = offset; i < len; i++) { + write (encoder, offset, offsetEnd) { + const end = this.arr.length - offsetEnd + encoder.writeLen(end - offset) + for (let i = offset; i < end; i++) { const c = this.arr[i] encoder.writeAny(c) } diff --git a/src/structs/ContentBinary.js b/src/structs/ContentBinary.js index a376dbbca..580b60fe2 100644 --- a/src/structs/ContentBinary.js +++ b/src/structs/ContentBinary.js @@ -71,9 +71,10 @@ export class ContentBinary { gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder - * @param {number} offset + * @param {number} _offset + * @param {number} _offsetEnd */ - write (encoder, offset) { + write (encoder, _offset, _offsetEnd) { encoder.writeBuf(this.content) } diff --git a/src/structs/ContentDeleted.js b/src/structs/ContentDeleted.js index 102bcdfa8..57b5504b4 100644 --- a/src/structs/ContentDeleted.js +++ b/src/structs/ContentDeleted.js @@ -78,9 +78,10 @@ export class ContentDeleted { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset + * @param {number} offsetEnd */ - write (encoder, offset) { - encoder.writeLen(this.len - offset) + write (encoder, offset, offsetEnd) { + encoder.writeLen(this.len - offset - offsetEnd) } /** diff --git a/src/structs/ContentDoc.js b/src/structs/ContentDoc.js index 8491272b1..6df705568 100644 --- a/src/structs/ContentDoc.js +++ b/src/structs/ContentDoc.js @@ -116,9 +116,10 @@ export class ContentDoc { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder - * @param {number} offset + * @param {number} _offset + * @param {number} _offsetEnd */ - write (encoder, offset) { + write (encoder, _offset, _offsetEnd) { encoder.writeString(this.doc.guid) encoder.writeAny(this.opts) } diff --git a/src/structs/ContentEmbed.js b/src/structs/ContentEmbed.js index 4cb2f9220..6d6ce53a5 100644 --- a/src/structs/ContentEmbed.js +++ b/src/structs/ContentEmbed.js @@ -74,9 +74,10 @@ export class ContentEmbed { gc (_tr) {} /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder - * @param {number} offset + * @param {number} _offset + * @param {number} _offsetEnd */ - write (encoder, offset) { + write (encoder, _offset, _offsetEnd) { encoder.writeJSON(this.embed) } diff --git a/src/structs/ContentFormat.js b/src/structs/ContentFormat.js index d79aa8855..1046d2556 100644 --- a/src/structs/ContentFormat.js +++ b/src/structs/ContentFormat.js @@ -83,8 +83,9 @@ export class ContentFormat { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} _offset + * @param {number} _offsetEnd */ - write (encoder, _offset) { + write (encoder, _offset, _offsetEnd) { encoder.writeKey(this.key) encoder.writeJSON(this.value) } diff --git a/src/structs/ContentJSON.js b/src/structs/ContentJSON.js index 175917561..f20d7306f 100644 --- a/src/structs/ContentJSON.js +++ b/src/structs/ContentJSON.js @@ -79,11 +79,12 @@ export class ContentJSON { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset + * @param {number} offsetEnd */ - write (encoder, offset) { - const len = this.arr.length - encoder.writeLen(len - offset) - for (let i = offset; i < len; i++) { + write (encoder, offset, offsetEnd) { + const end = this.arr.length - offsetEnd + encoder.writeLen(end - offset) + for (let i = offset; i < end; i++) { const c = this.arr[i] encoder.writeString(c === undefined ? 'undefined' : JSON.stringify(c)) } diff --git a/src/structs/ContentString.js b/src/structs/ContentString.js index a2f5f3684..6f1064b5a 100644 --- a/src/structs/ContentString.js +++ b/src/structs/ContentString.js @@ -90,9 +90,10 @@ export class ContentString { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset + * @param {number} offsetEnd */ - write (encoder, offset) { - encoder.writeString(offset === 0 ? this.str : this.str.slice(offset)) + write (encoder, offset, offsetEnd) { + encoder.writeString((offset === 0 && offsetEnd === 0) ? this.str : this.str.slice(offset, this.str.length - offsetEnd)) } /** diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index 13a09cd05..a69677d8a 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -149,8 +149,9 @@ export class ContentType { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} _offset + * @param {number} _offsetEnd */ - write (encoder, _offset) { + write (encoder, _offset, _offsetEnd) { this.type._write(encoder) } diff --git a/src/structs/GC.js b/src/structs/GC.js index 8f1d2f9e4..7ee1bc338 100644 --- a/src/structs/GC.js +++ b/src/structs/GC.js @@ -3,6 +3,7 @@ import { addStruct, addStructToIdSet, addToIdSet, + createID, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction // eslint-disable-line } from '../internals.js' @@ -47,10 +48,11 @@ export class GC extends AbstractStruct { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {number} offset + * @param {number} offsetEnd */ - write (encoder, offset) { + write (encoder, offset, offsetEnd) { encoder.writeInfo(structGCRefNumber) - encoder.writeLen(this.length - offset) + encoder.writeLen(this.length - offset - offsetEnd) } /** @@ -68,9 +70,11 @@ export class GC extends AbstractStruct { * If this feature is required in the future, then need to try to merge this struct after * transaction. * - * @param {number} _diff + * @param {number} diff */ - splice (_diff) { - return this + splice (diff) { + const other = new GC(createID(this.id.client, this.id.clock + diff), this.length - diff) + this.length = diff + return other } } diff --git a/src/structs/Item.js b/src/structs/Item.js index 042c971c4..0efc8e252 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -22,7 +22,6 @@ import { readContentType, addChangedTypeToTransaction, addStructToIdSet, - Skip, IdSet, StackItem, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, ContentType, ContentDeleted, StructStore, ID, AbstractType, Transaction, // eslint-disable-line } from '../internals.js' @@ -126,6 +125,26 @@ export const splitItem = (transaction, leftItem, diff) => { return rightItem } +/** + * More generalized version of splitItem. Split leftStruct into two structs + * @param {Transaction?} transaction + * @param {AbstractStruct} leftStruct + * @param {number} diff + * @return {GC|Item} + * + * @function + * @private + */ +export const splitStruct = (transaction, leftStruct, diff) => { + if (leftStruct instanceof Item) { + return splitItem(transaction, leftStruct, diff) + } else { + const rightItem = leftStruct.splice(diff) + transaction?._mergeStructs.push(rightItem) + return rightItem + } +} + /** * @param {Array} stack * @param {ID} id @@ -409,11 +428,6 @@ export class Item extends AbstractStruct { this.parent = /** @type {ContentType} */ (parentItem.content).type } } - // @todo remove thgis - if (this.left instanceof Skip || this.right instanceof Skip || this.parent instanceof Skip) { - debugger - throw new Error('dtruinae') - } return null } @@ -634,7 +648,7 @@ export class Item extends AbstractStruct { } /** - * @param {Transaction} tr + * @param {Transaction} tr * @param {boolean} parentGCd */ gc (tr, parentGCd) { @@ -657,8 +671,9 @@ export class Item extends AbstractStruct { * * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder The encoder to write data to. * @param {number} offset + * @param {number} offsetEnd */ - write (encoder, offset) { + write (encoder, offset, offsetEnd) { const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin const rightOrigin = this.rightOrigin const parentSub = this.parentSub @@ -700,7 +715,7 @@ export class Item extends AbstractStruct { encoder.writeString(parentSub) } } - this.content.write(encoder, offset) + this.content.write(encoder, offset, offsetEnd) } } @@ -808,8 +823,9 @@ export class AbstractContent { /** * @param {UpdateEncoderV1 | UpdateEncoderV2} _encoder * @param {number} _offset + * @param {number} _offsetEnd */ - write (_encoder, _offset) { + write (_encoder, _offset, _offsetEnd) { throw error.methodUnimplemented() } diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 3381e250c..c116f3f9f 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -62,7 +62,7 @@ export class Doc extends ObservableV2 { /** * @param {DocOpts} opts configuration */ - constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true, isSuggestionDoc = false} = {}) { + constructor ({ guid = random.uuidv4(), collectionid = null, gc = true, gcFilter = () => true, meta = null, autoLoad = false, shouldLoad = true, isSuggestionDoc = false } = {}) { super() this.gc = gc this.gcFilter = gcFilter diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index fcf9ea0a2..c585a9a24 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -184,7 +184,7 @@ export const createDocFromSnapshot = (originDoc, snapshot, newDoc = new Doc()) = // first clock written is 0 encoding.writeVarUint(encoder.restEncoder, 0) for (let i = 0; i <= lastStructIndex; i++) { - structs[i].write(encoder, 0) + structs[i].write(encoder, 0, 0) } } writeIdSet(encoder, ds) diff --git a/src/utils/StructSet.js b/src/utils/StructSet.js index 5f602810b..4af317481 100644 --- a/src/utils/StructSet.js +++ b/src/utils/StructSet.js @@ -83,6 +83,7 @@ export const readStructSet = (decoder, doc) => { * @param {IdSet} exclude */ export const removeRangesFromStructSet = (ss, exclude) => { + // @todo walk through ss instead to reduce iterations exclude.clients.forEach((range, client) => { const structs = /** @type {StructRange} */ (ss.clients.get(client))?.refs if (structs != null) { diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index fdcd7a5a7..78b734a75 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -5,7 +5,8 @@ import { createIdSet, Transaction, ID, Item, // eslint-disable-line Skip, - createID + createID, + splitStruct } from '../internals.js' import * as math from 'lib0/math' @@ -198,8 +199,8 @@ export const getItem = /** @type {function(StructStore,ID):Item} */ (find) export const findIndexCleanStart = (transaction, structs, clock) => { const index = findIndexSS(structs, clock) const struct = structs[index] - if (struct.id.clock < clock && struct.constructor !== GC) { - structs.splice(index + 1, 0, struct instanceof Item ? splitItem(transaction, struct, clock - struct.id.clock) : struct.splice(clock - struct.id.clock)) + if (struct.id.clock < clock) { + structs.splice(index + 1, 0, splitStruct(transaction, struct, clock - struct.id.clock)) return index + 1 } return index diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 98caf9797..f6b59095b 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -10,8 +10,7 @@ import { generateNewClientId, createID, cleanupYTextAfterTransaction, - IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc - // insertIntoIdSet + IdSet, UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 163cafabf..285e82598 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -37,7 +37,8 @@ import { removeRangesFromStructSet, createIdSet, StructSet, IdSet, DSDecoderV2, Doc, Transaction, GC, Item, StructStore, // eslint-disable-line - createID + createID, + IdRange } from '../internals.js' import * as encoding from 'lib0/encoding' @@ -50,24 +51,57 @@ import * as array from 'lib0/array' * @param {UpdateEncoderV1 | UpdateEncoderV2} encoder * @param {Array} structs All structs by `client` * @param {number} client - * @param {number} clock write structs starting with `ID(client,clock)` + * @param {Array} idranges * * @function */ -const writeStructs = (encoder, structs, client, clock) => { - // write first id - clock = math.max(clock, structs[0].id.clock) // make sure the first id exists - const startNewStructs = findIndexSS(structs, clock) +const writeStructs = (encoder, structs, client, idranges) => { + let structsToWrite = 0 // this accounts for the skips + /** + * @type {Array<{ start: number, end: number, startClock: number, endClock: number }>} + */ + const indexRanges = [] + const firstPossibleClock = structs[0].id.clock + const lastStruct = array.last(structs) + const lastPossibleClock = lastStruct.id.clock + lastStruct.length + idranges.forEach(idrange => { + const startClock = math.max(idrange.clock, firstPossibleClock) + const endClock = math.min(idrange.clock + idrange.len, lastPossibleClock) + if (startClock >= endClock) return // structs for this range do not exist + // inclusive start + const start = findIndexSS(structs, startClock) + // exclusive end + const end = findIndexSS(structs, endClock - 1) + 1 + structsToWrite += end - start + indexRanges.push({ + start, + end, + startClock, + endClock + }) + }) + structsToWrite += idranges.length - 1 + // start writing with this clock. this is updated to the next clock that we expect to write + let clock = indexRanges[0].startClock // write # encoded structs - encoding.writeVarUint(encoder.restEncoder, structs.length - startNewStructs) + encoding.writeVarUint(encoder.restEncoder, structsToWrite) encoder.writeClient(client) + // write clock encoding.writeVarUint(encoder.restEncoder, clock) - const firstStruct = structs[startNewStructs] - // write first struct with an offset - firstStruct.write(encoder, clock - firstStruct.id.clock) - for (let i = startNewStructs + 1; i < structs.length; i++) { - structs[i].write(encoder, 0) - } + indexRanges.forEach(indexRange => { + const skipLen = indexRange.startClock - clock + if (skipLen > 0) { + new Skip(createID(client, clock), skipLen).write(encoder, 0) + clock += skipLen + } + for (let i = indexRange.start; i < indexRange.end; i++) { + const struct = structs[i] + const structEnd = struct.id.clock + struct.length + const offsetEnd = math.max(structEnd - indexRange.endClock, 0) + struct.write(encoder, clock - struct.id.clock, offsetEnd) + clock = structEnd - offsetEnd + } + }) } /** @@ -97,7 +131,9 @@ export const writeClientsStructs = (encoder, store, _sm) => { // Write items with higher client ids first // This heavily improves the conflict algorithm. array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => { - writeStructs(encoder, /** @type {Array} */ (store.clients.get(client)), client, clock) + const structs = /** @type {Array} */ (store.clients.get(client)) + const lastStruct = structs[structs.length - 1] + writeStructs(encoder, structs, client, [new IdRange(clock, lastStruct.id.clock + lastStruct.length - clock)]) }) } @@ -117,7 +153,9 @@ export const writeStructsFromIdSet = (encoder, store, idset) => { // Write items with higher client ids first // This heavily improves the conflict algorithm. array.from(idset.clients.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, ids]) => { - writeStructs(encoder, /** @type {Array} */ (store.clients.get(client)), client, ids.getIds()[0].clock) + const idRanges = ids.getIds() + const structs = /** @type {Array} */ (store.clients.get(client)) + writeStructs(encoder, structs, client, idRanges) }) } diff --git a/src/utils/updates.js b/src/utils/updates.js index 558c9c2c7..ba6ef59d2 100644 --- a/src/utils/updates.js +++ b/src/utils/updates.js @@ -534,7 +534,7 @@ const writeStructToLazyStructWriter = (lazyWriter, struct, offset) => { // write startClock encoding.writeVarUint(lazyWriter.encoder.restEncoder, struct.id.clock + offset) } - struct.write(lazyWriter.encoder, offset) + struct.write(lazyWriter.encoder, offset, 0) lazyWriter.written++ } /** diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 40ccc58a3..f68c19785 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -180,7 +180,6 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { for (let j = 1; j < updates.length; j++) { const partMerged = enc.mergeUpdates(updates.slice(j)) const partMeta = enc.parseUpdateMeta(partMerged) - const targetSV = enc.encodeStateVectorFromUpdate(enc.mergeUpdates(updates.slice(0, j))) const diffed = enc.diffUpdate(mergedUpdates, targetSV) const diffedMeta = enc.parseUpdateMeta(diffed) From 74a5a371802fd2507c752fb8f17693964276cb8d Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 8 Jun 2025 02:17:23 +0200 Subject: [PATCH 331/362] DiffAttributionManager supports accepting suggestions --- src/utils/AttributionManager.js | 48 +++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index b8ca4b02e..9e6cbc45a 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -13,12 +13,14 @@ import { mergeIdMaps, createID, mergeIdSets, - IdSet, Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line + ID, IdSet, Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line applyUpdate, writeIdSet, UpdateEncoderV1, transact, - createMaybeAttrRange + createMaybeAttrRange, + createIdSet, + writeStructsFromIdSet } from '../internals.js' import * as error from 'lib0/error' @@ -327,6 +329,48 @@ export class DiffAttributionManager extends ObservableV2 { this._nextDoc.off('afterTransaction', this._afterTrListener) } + /** + * @param {ID} start + * @param {ID?} end + */ + acceptChanges (start, end = start) { + const encoder = new UpdateEncoderV1() + const store = this._nextDoc.store + const inserts = createIdSet() + const deletes = createIdSet() + /** + * @type {Item?} + */ + let item = getItem(store, start) + const endItem = start === end ? item : (end == null ? null : getItem(store, end)) + // walk to the left and find first un-attributed change that is rendered + while (item.left != null) { + item = item.left + if (!item.deleted) { + const slice = this.inserts.slice(item.id.client, item.id.clock, item.length) + if (slice.some(s => s.attrs === null)) { + break + } + } + } + let foundEndItem = false + while (item != null) { + inserts.add(item.id.client, item.id.clock, item.length) + if (item.deleted) { + deletes.add(item.id.client, item.id.clock, item.length) + } + foundEndItem ||= item === endItem + if (foundEndItem && !item.deleted && this.inserts.slice(item.id.client, item.id.clock, item.length).some(s => s.attrs === null)) { + break + } + item = item.right + } + writeStructsFromIdSet(encoder, this._nextDoc.store, inserts) + writeIdSet(encoder, deletes) + const acceptUpdate = encoder.toUint8Array() + applyUpdate(this._prevDoc, acceptUpdate) + } + /** * @param {Array>} contents - where to write the result * @param {number} client From 10a019806a8a40ed9925b51f79a2052833a8aa60 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 9 Jun 2025 19:40:07 +0200 Subject: [PATCH 332/362] reject suggestion working --- src/utils/AttributionManager.js | 159 ++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 36 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 9e6cbc45a..6bb0af78a 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -20,7 +20,13 @@ import { transact, createMaybeAttrRange, createIdSet, - writeStructsFromIdSet + writeStructsFromIdSet, + UndoManager, + StackItem, + getItemCleanStart, + Transaction, + StructStore, + intersectSets } from '../internals.js' import * as error from 'lib0/error' @@ -226,6 +232,98 @@ export class NoAttributionsManager extends ObservableV2 { export const noAttributionsManager = new NoAttributionsManager() +/** + * @param {StructStore} store + * @param {number} client + * @param {number} clock + * @param {number} len + */ +const getItemContent = (store, client, clock, len) => { + // Retrieved item is never more fragmented than the newer item. + const prevItem = getItem(store, createID(client, clock)) + const diffStart = clock - prevItem.id.clock + let content = prevItem.length > 1 ? prevItem.content.copy() : prevItem.content + // trim itemContent to the correct size. + if (diffStart > 0) { + content = content.splice(diffStart) + } + if (len > 0) { + content.splice(len) + } + return content +} + +/** + * @param {Transaction?} tr - only specify this if you want to fill the content of deleted content + * @param {DiffAttributionManager} am + * @param {ID} start + * @param {ID} end + * @param {boolean} collectAll - collect as many items as possible. Accept adding redundant changes. + */ +const collectSuggestedChanges = (tr, am, start, end, collectAll) => { + const inserts = createIdSet() + const deletes = createIdSet() + const store = am._nextDoc.store + /** + * @type {Item?} + */ + let item = getItem(store, start) + const endItem = start === end ? item : (end == null ? null : getItem(store, end)) + // walk to the left and find first un-attributed change that is rendered + while (item.left != null) { + item = item.left + if (!item.deleted) { + const slice = am.inserts.slice(item.id.client, item.id.clock, item.length) + if (slice.some(s => s.attrs === null)) { + for (let i = slice.length -1; i >= 0; i--) { + const s = slice[i] + if (s.attrs == null) break + inserts.add(item.id.client, s.clock, s.len) + } + item = item.right + break + } + } + } + let foundEndItem = false + itemLoop: while (item != null) { + const itemClient = item.id.client + const slice = (item.deleted ? am.deletes : am.inserts).slice(itemClient, item.id.clock, item.length) + foundEndItem ||= item === endItem + if (item.deleted) { + // item probably gc'd content. Need to split item and fill with content again + for (let i = slice.length - 1; i >= 0; i--) { + const s = slice[i] + if (s.attrs != null || collectAll) { + deletes.add(itemClient, s.clock, s.len) + if (collectAll) { + // in case item has been added and deleted this might be necessary. the forked document + // will automatically filter this if it doesn't have it already. + inserts.add(itemClient, s.clock, s.len) + } + } + if (tr != null) { + const splicedItem = getItemCleanStart(tr, createID(itemClient, s.clock)) + if (s.attrs != null) { + splicedItem.content = getItemContent(am._prevDocStore, itemClient, s.clock, s.len) + } + } + } + } else { + for (let i = 0; i < slice.length; i++) { + const s = slice[i] + if (s.attrs != null) { + inserts.add(itemClient, s.clock, s.len) + } else if (foundEndItem) { + break itemLoop + } + } + } + item = item.right + } + return { inserts, deletes } +} + /** * @implements AbstractAttributionManager * @@ -276,8 +374,9 @@ export class DiffAttributionManager extends ObservableV2 { } else { this.deletes = diffIdMap(this.deletes, tr.deleteSet) } - // @todo fire update ranges on `tr.insertSet` and `tr.deleteSet` - this.emit('change', [mergeIdSets([tr.insertSet, tr.deleteSet]), tr.origin, tr.local]) + // fire event of "changed" attributions. exclude items that were added & deleted in the same + // transaction + this.emit('change', [diffIdSet(mergeIdSets([tr.insertSet, tr.deleteSet]), intersectSets(tr.insertSet, tr.deleteSet)), tr.origin, tr.local]) }) // changes from prevDoc should always flow into suggestionDoc // changes from suggestionDoc only flow into ydoc if suggestion-mode is disabled @@ -331,44 +430,32 @@ export class DiffAttributionManager extends ObservableV2 { /** * @param {ID} start - * @param {ID?} end + * @param {ID} end */ acceptChanges (start, end = start) { + const { inserts, deletes } = collectSuggestedChanges(null, this, start, end, true) const encoder = new UpdateEncoderV1() - const store = this._nextDoc.store - const inserts = createIdSet() - const deletes = createIdSet() - /** - * @type {Item?} - */ - let item = getItem(store, start) - const endItem = start === end ? item : (end == null ? null : getItem(store, end)) - // walk to the left and find first un-attributed change that is rendered - while (item.left != null) { - item = item.left - if (!item.deleted) { - const slice = this.inserts.slice(item.id.client, item.id.clock, item.length) - if (slice.some(s => s.attrs === null)) { - break - } - } - } - let foundEndItem = false - while (item != null) { - inserts.add(item.id.client, item.id.clock, item.length) - if (item.deleted) { - deletes.add(item.id.client, item.id.clock, item.length) - } - foundEndItem ||= item === endItem - if (foundEndItem && !item.deleted && this.inserts.slice(item.id.client, item.id.clock, item.length).some(s => s.attrs === null)) { - break - } - item = item.right - } writeStructsFromIdSet(encoder, this._nextDoc.store, inserts) writeIdSet(encoder, deletes) - const acceptUpdate = encoder.toUint8Array() - applyUpdate(this._prevDoc, acceptUpdate) + applyUpdate(this._prevDoc, encoder.toUint8Array()) + } + + /** + * @param {ID} start + * @param {ID} end + */ + rejectChanges (start, end = start) { + this._nextDoc.transact(tr => { + const { inserts, deletes } = collectSuggestedChanges(tr, this, start, end, false) + const encoder = new UpdateEncoderV1() + writeStructsFromIdSet(encoder, this._nextDoc.store, inserts) + writeIdSet(encoder, deletes) + const um = new UndoManager(this._nextDoc) + um.undoStack.push(new StackItem(deletes, inserts)) + um.undo() + um.destroy() + }) + this.acceptChanges(start, end) } /** From a9f802d77e6a4287830f269f6ada4aac084c6519 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 9 Jun 2025 21:21:33 +0200 Subject: [PATCH 333/362] fixed a bunch of issues related to attribution of formats --- src/types/YText.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 20dc66623..7bde77541 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -972,7 +972,7 @@ export class YText extends AbstractType { const renderDelete = c.render && c.deleted // existing content that should be retained, only adding changed attributes const retainContent = !c.render && (!c.deleted || c.attrs != null) - const attribution = renderContent ? createAttributionFromAttributionItems(c.attrs, c.deleted) : null + const attribution = (renderContent || c.content.constructor === ContentFormat) ? createAttributionFromAttributionItems(c.attrs, c.deleted) : null switch (c.content.constructor) { case ContentDeleted: { if (renderDelete) d.delete(c.content.getLength()) @@ -1032,15 +1032,15 @@ export class YText extends AbstractType { if (renderContent || renderDelete) { if (c.deleted) { // content was deleted, but is possibly attributed - if (equalAttrs(value, currAttrVal)) { - // nop - } else if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { - delete changedAttributes[key] - } else { - changedAttributes[key] = currAttrVal + if (!equalAttrs(value, currAttrVal)) { // do nothing if nothing changed + if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { + delete changedAttributes[key] + } else { + changedAttributes[key] = currAttrVal + } + // current attributes doesn't change + previousAttributes[key] = value } - // current attributes doesn't change - previousAttributes[key] = value } else { // !c.deleted // content was inserted, and is possibly attributed if (equalAttrs(value, currAttrVal)) { From 9161a9682c632ec9999e181a11ecc8eb756f4860 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 10 Jun 2025 03:10:32 +0200 Subject: [PATCH 334/362] [delta] useAttributes doesn't clean up empty objects --- src/types/YText.js | 1 + src/utils/AttributionManager.js | 4 +++- src/utils/Delta.js | 4 ++-- src/utils/RelativePosition.js | 1 - 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 7bde77541..25eadf4ce 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1081,6 +1081,7 @@ export class YText extends AbstractType { */ const formattingAttribution = object.assign({}, d.usedAttribution) const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + debugger if (value === null) { delete attributesChanged[key] } else { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 6bb0af78a..0f333e830 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -56,7 +56,9 @@ import * as encoding from 'lib0/encoding' * @return {Attribution?} */ export const createAttributionFromAttributionItems = (attrs, deleted) => { - if (attrs == null) return null + if (attrs == null) { + return null + } /** * @type {Attribution} */ diff --git a/src/utils/Delta.js b/src/utils/Delta.js index 5352bac1f..d5fc0dd19 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -300,7 +300,7 @@ export class DeltaBuilder extends AbstractDelta { * @return {this} */ useAttributes (attributes) { - this.usedAttributes = object.isEmpty(attributes) ? null : object.assign({}, attributes) + this.usedAttributes = attributes return this } @@ -345,7 +345,7 @@ export class DeltaBuilder extends AbstractDelta { * @param {Attribution?} attribution */ useAttribution (attribution) { - this.usedAttribution = object.isEmpty(attribution) ? null : object.assign({}, attribution) + this.usedAttribution = attribution return this } diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index a3ec3fdfe..ddee880c4 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -72,7 +72,6 @@ export class RelativePosition { * @type {number} */ this.assoc = assoc - this.item && console.log('created relpos', this.item) // @todo remove } } From d6a3e637e0158ddfdafa238db2086ed4625f00f2 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 11 Jun 2025 00:00:07 +0200 Subject: [PATCH 335/362] fixes for accepting / rejecting suggestions --- src/types/YText.js | 1 - src/utils/AttributionManager.js | 25 ++++++++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 25eadf4ce..7bde77541 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1081,7 +1081,6 @@ export class YText extends AbstractType { */ const formattingAttribution = object.assign({}, d.usedAttribution) const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - debugger if (value === null) { delete attributesChanged[key] } else { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 0f333e830..d1f903647 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -26,7 +26,8 @@ import { getItemCleanStart, Transaction, StructStore, - intersectSets + intersectSets, + ContentFormat } from '../internals.js' import * as error from 'lib0/error' @@ -249,7 +250,7 @@ const getItemContent = (store, client, clock, len) => { if (diffStart > 0) { content = content.splice(diffStart) } - if (len > 0) { + if (len < content.getLength()) { content.splice(len) } return content @@ -266,14 +267,24 @@ const collectSuggestedChanges = (tr, am, start, end, collectAll) => { const inserts = createIdSet() const deletes = createIdSet() const store = am._nextDoc.store + /** + * make sure to collect suggestions until all formats are closed + * @type {Set} + */ + const openedCollectedFormats = new Set() /** * @type {Item?} */ let item = getItem(store, start) const endItem = start === end ? item : (end == null ? null : getItem(store, end)) + // walk to the left and find first un-attributed change that is rendered while (item.left != null) { item = item.left + if (item.content instanceof ContentFormat && item.content.value == null) { + item = item.right + break + } if (!item.deleted) { const slice = am.inserts.slice(item.id.client, item.id.clock, item.length) if (slice.some(s => s.attrs === null)) { @@ -312,11 +323,19 @@ const collectSuggestedChanges = (tr, am, start, end, collectAll) => { } } } else { + if (item.content instanceof ContentFormat) { + const {key, value} = item.content + if (value == null) { + openedCollectedFormats.delete(key) + } else { + openedCollectedFormats.add(key) + } + } for (let i = 0; i < slice.length; i++) { const s = slice[i] if (s.attrs != null) { inserts.add(itemClient, s.clock, s.len) - } else if (foundEndItem) { + } else if (foundEndItem && openedCollectedFormats.size === 0) { break itemLoop } } From 5c5139bbba8716ce4dc8540e6f62955cf3a7d6cb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 11 Jun 2025 17:39:49 +0200 Subject: [PATCH 336/362] handle another attribution edge case for formatting --- src/types/YText.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 7bde77541..86782df6a 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1075,13 +1075,13 @@ export class YText extends AbstractType { previousAttributes[key] = value } // # Update Attributions - if (attribution != null) { + if (attribution != null || d.usedAttribution?.attributes?.[key] != null) { /** * @type {import('../utils/Delta.js').Attribution} */ const formattingAttribution = object.assign({}, d.usedAttribution) const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - if (value === null) { + if (value === null || attribution == null) { delete attributesChanged[key] } else { const by = attributesChanged[key] = (attributesChanged[key]?.slice() ?? []) @@ -1089,12 +1089,14 @@ export class YText extends AbstractType { const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) if (attributedAt) formattingAttribution.attributedAt = attributedAt } - if (object.isEmpty(attributesChanged)) { - d.useAttribution(null) - } else { - const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt != null) formattingAttribution.attributedAt = attributedAt - d.useAttribution(formattingAttribution) + if (attribution != null) { + if (object.isEmpty(attributesChanged)) { + d.useAttribution(null) + } else { + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + d.useAttribution(formattingAttribution) + } } } break From a1a0f441eaeab4a5b96515f01b2e77286878db12 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 11 Jun 2025 17:49:30 +0200 Subject: [PATCH 337/362] lint --- src/utils/AttributionManager.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index d1f903647..d75c1df99 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -13,7 +13,6 @@ import { mergeIdMaps, createID, mergeIdSets, - ID, IdSet, Item, Snapshot, Doc, AbstractContent, IdMap, // eslint-disable-line applyUpdate, writeIdSet, UpdateEncoderV1, @@ -24,10 +23,9 @@ import { UndoManager, StackItem, getItemCleanStart, - Transaction, - StructStore, intersectSets, - ContentFormat + ContentFormat, + StructStore, Transaction, ID, IdSet, Item, Snapshot, Doc, AbstractContent, IdMap // eslint-disable-line } from '../internals.js' import * as error from 'lib0/error' @@ -288,7 +286,7 @@ const collectSuggestedChanges = (tr, am, start, end, collectAll) => { if (!item.deleted) { const slice = am.inserts.slice(item.id.client, item.id.clock, item.length) if (slice.some(s => s.attrs === null)) { - for (let i = slice.length -1; i >= 0; i--) { + for (let i = slice.length - 1; i >= 0; i--) { const s = slice[i] if (s.attrs == null) break inserts.add(item.id.client, s.clock, s.len) @@ -299,6 +297,7 @@ const collectSuggestedChanges = (tr, am, start, end, collectAll) => { } } let foundEndItem = false + // eslint-disable-next-line itemLoop: while (item != null) { const itemClient = item.id.client const slice = (item.deleted ? am.deletes : am.inserts).slice(itemClient, item.id.clock, item.length) @@ -324,7 +323,7 @@ const collectSuggestedChanges = (tr, am, start, end, collectAll) => { } } else { if (item.content instanceof ContentFormat) { - const {key, value} = item.content + const { key, value } = item.content if (value == null) { openedCollectedFormats.delete(key) } else { @@ -336,6 +335,7 @@ const collectSuggestedChanges = (tr, am, start, end, collectAll) => { if (s.attrs != null) { inserts.add(itemClient, s.clock, s.len) } else if (foundEndItem && openedCollectedFormats.size === 0) { + // eslint-disable-next-line break itemLoop } } From 05c79b98d9e3bc56497ef0b05eecb8543a1951f1 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 11 Jun 2025 17:52:01 +0200 Subject: [PATCH 338/362] 14.0.0-6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index cab1792a4..ffe750820 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-5", + "version": "14.0.0-6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-5", + "version": "14.0.0-6", "license": "MIT", "dependencies": { "lib0": "^0.2.107" diff --git a/package.json b/package.json index f099f1bdf..1d3ab903b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-5", + "version": "14.0.0-6", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 5e4d240e194b44a7314e36f63e1f15aa32e03c61 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 12 Jun 2025 18:54:47 +0200 Subject: [PATCH 339/362] [suggested formatting] implement previousUnattributedAttributes approach --- src/types/YText.js | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 86782df6a..5a79594ef 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -935,6 +935,16 @@ export class YText extends AbstractType { */ let changedAttributes = {} // saves changed attributes for retain let usingChangedAttributes = false + /** + * Logic for formatting attribute attribution + * Everything that comes after an formatting attribute is formatted by the user that created it. + * Two exceptions: + * - the user resets formatting to the previously known formatting that is not attributed + * - the user deletes a formatting attribute and hence restores the previously known formatting + * that is not attributed. + * @type {import('../utils/Delta.js').FormattingAttributes} + */ + let previousUnattributedAttributes = {} // contains previously known unattributed formatting /** * @type {import('../utils/Delta.js').FormattingAttributes} */ @@ -1016,6 +1026,9 @@ export class YText extends AbstractType { case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (c.content) const currAttrVal = currentAttributes[key] ?? null + if (attribution != null && (c.deleted || !previousUnattributedAttributes.hasOwnProperty(key))) { + previousUnattributedAttributes[key] = c.deleted ? value : currAttrVal + } // @todo write a function "updateCurrentAttributes" and "updateChangedAttributes" // # Update Attributes if (renderContent || renderDelete) { @@ -1075,28 +1088,30 @@ export class YText extends AbstractType { previousAttributes[key] = value } // # Update Attributions - if (attribution != null || d.usedAttribution?.attributes?.[key] != null) { + if (attribution != null || previousUnattributedAttributes.hasOwnProperty(key)) { /** * @type {import('../utils/Delta.js').Attribution} */ const formattingAttribution = object.assign({}, d.usedAttribution) - const attributesChanged = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - if (value === null || attribution == null) { - delete attributesChanged[key] + const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + + if (attribution == null || equalAttrs(previousUnattributedAttributes[key], currentAttributes[key] ?? null)) { + // an unattributed formatting attribute was found or an attributed formatting + // attribute was found that resets to the previous status + delete changedAttributedAttributes[key] + delete previousUnattributedAttributes[key] } else { - const by = attributesChanged[key] = (attributesChanged[key]?.slice() ?? []) + const by = changedAttributedAttributes[key] = (changedAttributedAttributes[key]?.slice() ?? []) by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) if (attributedAt) formattingAttribution.attributedAt = attributedAt } - if (attribution != null) { - if (object.isEmpty(attributesChanged)) { - d.useAttribution(null) - } else { - const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt != null) formattingAttribution.attributedAt = attributedAt - d.useAttribution(formattingAttribution) - } + if (object.isEmpty(changedAttributedAttributes)) { + d.useAttribution(null) + } else if (attribution != null) { + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + d.useAttribution(formattingAttribution) } } break From 6c7866147bc84024d89cf3894401974500082197 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 12 Jun 2025 19:06:30 +0200 Subject: [PATCH 340/362] lint --- src/types/YText.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 5a79594ef..5ba588b82 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -944,7 +944,7 @@ export class YText extends AbstractType { * that is not attributed. * @type {import('../utils/Delta.js').FormattingAttributes} */ - let previousUnattributedAttributes = {} // contains previously known unattributed formatting + const previousUnattributedAttributes = {} // contains previously known unattributed formatting /** * @type {import('../utils/Delta.js').FormattingAttributes} */ @@ -1026,7 +1026,7 @@ export class YText extends AbstractType { case ContentFormat: { const { key, value } = /** @type {ContentFormat} */ (c.content) const currAttrVal = currentAttributes[key] ?? null - if (attribution != null && (c.deleted || !previousUnattributedAttributes.hasOwnProperty(key))) { + if (attribution != null && (c.deleted || !object.hasProperty(previousUnattributedAttributes, key))) { previousUnattributedAttributes[key] = c.deleted ? value : currAttrVal } // @todo write a function "updateCurrentAttributes" and "updateChangedAttributes" @@ -1088,13 +1088,12 @@ export class YText extends AbstractType { previousAttributes[key] = value } // # Update Attributions - if (attribution != null || previousUnattributedAttributes.hasOwnProperty(key)) { + if (attribution != null || object.hasProperty(previousUnattributedAttributes, key)) { /** * @type {import('../utils/Delta.js').Attribution} */ const formattingAttribution = object.assign({}, d.usedAttribution) const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - if (attribution == null || equalAttrs(previousUnattributedAttributes[key], currentAttributes[key] ?? null)) { // an unattributed formatting attribute was found or an attributed formatting // attribute was found that resets to the previous status From ab9e7262470c95dae34d26209443b95f90a1dfa9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 12 Jun 2025 19:08:09 +0200 Subject: [PATCH 341/362] 14.0.0-7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index ffe750820..7fd2680e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-6", + "version": "14.0.0-7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-6", + "version": "14.0.0-7", "license": "MIT", "dependencies": { "lib0": "^0.2.107" diff --git a/package.json b/package.json index 1d3ab903b..f6ba786cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-6", + "version": "14.0.0-7", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From e05d538f210137c95a1af59959bf94faa776670e Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 16 Jun 2025 12:15:40 +0200 Subject: [PATCH 342/362] documentation for attribution feature --- attribution-manager.md | 237 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 attribution-manager.md diff --git a/attribution-manager.md b/attribution-manager.md new file mode 100644 index 000000000..a90c6b1db --- /dev/null +++ b/attribution-manager.md @@ -0,0 +1,237 @@ + +# Attribution Feature + +The Attribution feature extends Yjs types to provide rich metadata about content +changes, including information about who created, deleted, or formatted content. +This enables powerful collaborative editing features such as authorship tracking +and change visualization. The information about who performed which changes can +be handled by a separate CRDT (which is part of the attribution manager). + +## Core Concepts + +### Attribution Manager + +The `attributionManager` is the central component that tracks and manages +attribution data. It must be passed to methods that support attribution to +enable the feature. + +Different implementations of AttributionManager are available for different use cases: +- `DiffingAttributionManager`: Highlights the differences between two Yjs documents +- `SnapshotAttributionManager`: Highlights the differences between two snapshots + +### Attributed Content + +Attributed content includes standard Yjs operations enhanced with attribution metadata: + +```javascript +// Standard content +[{ insert: 'hello world' }] + +// Attributed content +[ + { insert: 'hello', attribution: { insert: ['kevin'] } }, + { insert: ' world', attribution: { insert: ['alice'] } } +] +``` + +### Delete Attribution + +Deleted content is represented in attributed results to maintain authorship information and proper position tracking: + +```javascript +// Shows deleted content with attribution +[ + { insert: 'hello ', attribution: { delete: ['kevin'] } }, + { insert: 'world' } +] +``` + +## API Reference + +### YText + +#### `getDelta([attributionManager])` + +Returns the delta representation of the YText content, optionally with attribution information. + +**Parameters:** +- `attributionManager` (optional): The attribution manager instance + +**Returns:** +- Array of delta operations, with attribution metadata if `attributionManager` is provided + +**Examples:** + +```javascript +const ytext = new Y.Text() +// Content is inserted during collaborative editing +// Attribution is handled automatically by the server + +// Without attribution +const delta = ytext.getDelta() +// [{ insert: 'hello world' }] + +// With attribution +const attributedDelta = ytext.getDelta(attributionManager) +// [ +// { insert: 'hello', attribution: { insert: ['kevin'] } }, +// { insert: ' world', attribution: { insert: ['alice'] } } +// ] +``` + +#### `getContent([attributionManager])` + +Returns the content representation with optional attribution information. + +**Parameters:** +- `attributionManager` (optional): The attribution manager instance + +**Returns:** +- Content representation with attribution metadata if `attributionManager` is provided + +### YArray + +#### `getContent([attributionManager])` + +Returns the array content with optional attribution information for each element. + +**Parameters:** +- `attributionManager` (optional): The attribution manager instance + +**Returns:** +- Array content with attribution metadata if `attributionManager` is provided + +### YMap + +#### `getContent([attributionManager])` + +Returns the map content with optional attribution information for each key-value pair. + +**Parameters:** +- `attributionManager` (optional): The attribution manager instance + +**Returns:** +- Map content with attribution metadata if `attributionManager` is provided + +## Position Adjustments + +When working with attributed content, position calculations must account for deleted content that appears in the attributed representation but not in the standard representation. + +### Example: Position Adjustment + +```javascript +// Standard content (length: 5) +ytext.toString() // "world" + +// Attributed content (includes deleted content) +ytext.getDelta(attributionManager) +// [ +// { insert: 'hello ', attribution: { delete: ['kevin'] } }, // positions 0-5 +// { insert: 'world' } // positions 6-10 +// ] + +// To insert after "world": +// - Standard position: 5 (after "world") +// - Attributed position: 11 (after "world" accounting for deleted "hello ") +``` + +## Use Cases + +Events in Yjs are enhanced to work with attributed content, automatically adjusting positions when attribution is considered. + +### Event Position Adjustment + +When an `attributionManager` is used, event positions are automatically adjusted to account for deleted content. + +**Example:** + +```javascript +// Initial content: "hello world" +// User deletes "hello " (positions 0-6) +// Current visible content: "world" + +ytext.observe((event, transaction) => { + // User wants to insert "!" after "world" + + // Standard event (without attribution) + const standardDelta = event.getDelta() + // Shows insertion at position 5 (after "world" in visible content) + + // Attributed event (with attribution manager) + const attributedDelta = event.getDelta(attributionManager) + // Shows insertion at position 11 (accounting for deleted "hello ") + // [ + // { insert: 'hello ', attribution: { delete: ['kevin'] } }, + // { insert: 'world' }, + // { insert: '!' } // inserted at attributed position 11 + // ] +}) +``` + +## Use Cases + +### Authorship Visualization + +Display content with visual indicators of who created each part: + +```javascript +function renderWithAuthorship(ytext, attributionManager) { + const attributedDelta = ytext.getDelta(attributionManager) + + return attributedDelta.map(op => { + const author = op.attribution?.insert?.[0] || 'unknown' + const isDeleted = op.attribution?.delete + + return { + content: op.insert, + author, + isDeleted, + className: `author-${author} ${isDeleted ? 'deleted' : ''}` + } + }) +} +``` + +### Change Tracking + +Track who made specific changes to content: + +```javascript +function trackChanges(ytext, attributionManager) { + ytext.observe((event, transaction) => { + const changes = event.changes.getAttributedDelta?.(attributionManager) || event.changes.delta + + changes.forEach(change => { + if (change.attribution) { + console.log(`Change by ${change.attribution.insert?.[0] || change.attribution.delete?.[0]}:`, change) + } + }) + }) +} +``` + +## Best Practices + +### Attribution Manager Lifecycle + +- Create one attribution manager per document or collaboration session +- Ensure the attribution manager is consistently used across all operations +- Pass the same attribution manager instance to all methods that need attribution + +## Migration Guide + +### Upgrading Existing Code + +To add attribution support to existing Yjs applications: + +1. **Add attribution manager**: Create and configure an attribution manager +2. **Update method calls**: Add the attribution manager parameter to relevant method calls +3. **Handle attributed content**: Update code to handle the new attribution metadata format +4. **Adjust position calculations**: Update position calculations to account for deleted content + +### Backward Compatibility + +The Attribution feature is fully backward compatible: +- All existing methods work without the attribution manager parameter +- Existing code continues to work unchanged +- Attribution is opt-in and doesn't affect performance when not used From 5000fe58f92f24745c6a93125fffbed42afecdfe Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 23 Jun 2025 15:04:59 +0200 Subject: [PATCH 343/362] export createDelta --- src/internals.js | 1 + src/types/YText.js | 2 +- src/utils/AttributionManager.js | 2 +- src/utils/Delta.js | 24 ++++++++++++------------ src/utils/YEvent.js | 15 +++------------ 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/internals.js b/src/internals.js index 6d98c8516..d487ce140 100644 --- a/src/internals.js +++ b/src/internals.js @@ -19,6 +19,7 @@ export * from './utils/YEvent.js' export * from './utils/StructSet.js' export * from './utils/IdMap.js' export * from './utils/AttributionManager.js' +export * from './utils/Delta.js' export * from './types/AbstractType.js' export * from './types/YArray.js' diff --git a/src/types/YText.js b/src/types/YText.js index 5ba588b82..45e34f747 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1090,7 +1090,7 @@ export class YText extends AbstractType { // # Update Attributions if (attribution != null || object.hasProperty(previousUnattributedAttributes, key)) { /** - * @type {import('../utils/Delta.js').Attribution} + * @type {import('../utils/AttributionManager.js').Attribution} */ const formattingAttribution = object.assign({}, d.usedAttribution) const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index d75c1df99..a5bb81f22 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -77,7 +77,7 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { // eslint-disable-next-line no-fallthrough case 'insert': case 'delete': { - const as = /** @type {import('../utils/Delta.js').Attribution} */ (attribution) + const as = /** @type {import('../utils/Delta.js').Attribution_} */ (attribution) const ls = as[attr.name] = as[attr.name] ?? [] ls.push(attr.val) break diff --git a/src/utils/Delta.js b/src/utils/Delta.js index d5fc0dd19..cf9c54765 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -20,7 +20,7 @@ import * as error from 'lib0/error' */ /** - * @typedef {import('./AttributionManager.js').Attribution} Attribution + * @typedef {import('./AttributionManager.js').Attribution} Attribution_ */ /** @@ -32,14 +32,14 @@ import * as error from 'lib0/error' */ /** - * @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution }} DeltaJsonOp + * @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution_ } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution_ }} DeltaJsonOp */ export class InsertStringOp { /** * @param {string} insert * @param {FormattingAttributes|null} attributes - * @param {Attribution|null} attribution + * @param {Attribution_|null} attribution */ constructor (insert, attributes, attribution) { this.insert = insert @@ -73,7 +73,7 @@ export class InsertArrayOp { /** * @param {Array} insert * @param {FormattingAttributes|null} attributes - * @param {Attribution|null} attribution + * @param {Attribution_|null} attribution */ constructor (insert, attributes, attribution) { this.insert = insert @@ -107,7 +107,7 @@ export class InsertEmbedOp { /** * @param {Embeds} insert * @param {FormattingAttributes|null} attributes - * @param {Attribution|null} attribution + * @param {Attribution_|null} attribution */ constructor (insert, attributes, attribution) { this.insert = insert @@ -165,7 +165,7 @@ export class RetainOp { /** * @param {number} retain * @param {FormattingAttributes|null} attributes - * @param {Attribution|null} attribution + * @param {Attribution_|null} attribution */ constructor (retain, attributes, attribution) { this.retain = retain @@ -286,7 +286,7 @@ export class DeltaBuilder extends AbstractDelta { */ this.usedAttributes = null /** - * @type {Attribution?} + * @type {Attribution_?} */ this.usedAttribution = null /** @@ -323,9 +323,9 @@ export class DeltaBuilder extends AbstractDelta { } /** - * @template {keyof Attribution} NAME + * @template {keyof Attribution_} NAME * @param {NAME} name - * @param {Attribution[NAME]?} value + * @param {Attribution_[NAME]?} value */ updateUsedAttribution (name, value) { if (value == null) { @@ -342,7 +342,7 @@ export class DeltaBuilder extends AbstractDelta { } /** - * @param {Attribution?} attribution + * @param {Attribution_?} attribution */ useAttribution (attribution) { this.usedAttribution = attribution @@ -352,7 +352,7 @@ export class DeltaBuilder extends AbstractDelta { /** * @param {(TDeltaOp extends InsertStringOp ? string : never) | (TDeltaOp extends InsertEmbedOp ? (Embeds) : never) | (TDeltaOp extends InsertArrayOp ? Array : never) } insert * @param {FormattingAttributes?} attributes - * @param {Attribution?} attribution + * @param {Attribution_?} attribution * @return {this} */ insert (insert, attributes = null, attribution = null) { @@ -377,7 +377,7 @@ export class DeltaBuilder extends AbstractDelta { /** * @param {number} retain * @param {FormattingAttributes?} attributes - * @param {Attribution?} attribution + * @param {Attribution_?} attribution * @return {this} */ retain (retain, attributes = null, attribution = null) { diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 4a49ec902..3827fb180 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -1,20 +1,11 @@ import { - Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line + TextDelta, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line } from '../internals.js' import * as set from 'lib0/set' import * as array from 'lib0/array' import * as error from 'lib0/error' -/** - * @template {object} Embed - * @typedef {import('../utils/Delta.js').TextDelta} TextDelta - */ - -/** - * @typedef {import('../utils/Delta.js').Delta} Delta - */ - const errorComputeChanges = 'You must not compute changes after the event-handler fired.' /** @@ -151,7 +142,7 @@ export class YEvent { * unexpected behavior (incorrect computation of deltas). A safe way to collect changes * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. * - * @type {Delta} + * @type {import('./Delta.js').Delta} */ get delta () { return this.changes.delta @@ -175,7 +166,7 @@ export class YEvent { * unexpected behavior (incorrect computation of deltas). A safe way to collect changes * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. * - * @type {{added:Set,deleted:Set,keys:Map,delta:Delta}} + * @type {{added:Set,deleted:Set,keys:Map,delta:import('./Delta.js').Delta}} */ get changes () { let changes = this._changes From 496389e9df863bda18260846b014873b720f16df Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 23 Jun 2025 15:07:21 +0200 Subject: [PATCH 344/362] 14.0.0-8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7fd2680e7..7b34ac9b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-7", + "version": "14.0.0-8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-7", + "version": "14.0.0-8", "license": "MIT", "dependencies": { "lib0": "^0.2.107" diff --git a/package.json b/package.json index f6ba786cd..309db69c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-7", + "version": "14.0.0-8", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 72393e6ce8aef65cd96ff18d51de409f01453847 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 7 Jul 2025 20:23:50 +0200 Subject: [PATCH 345/362] work on new deltas with modifiers --- src/types/AbstractType.js | 21 +- src/types/YArray.js | 4 +- src/types/YMap.js | 10 +- src/types/YText.js | 18 +- src/types/YXmlElement.js | 16 +- src/types/YXmlFragment.js | 4 +- src/types/YXmlText.js | 4 +- src/utils/Delta.js | 470 +++++++++++++++++++++++++++++++++-- tests/compatibility.tests.js | 2 +- tests/delta.tests.js | 89 +++++++ tests/undo-redo.tests.js | 12 +- tests/updates.tests.js | 4 +- tests/y-array.tests.js | 2 +- tests/y-map.tests.js | 6 +- tests/y-text.tests.js | 64 ++--- tests/y-xml.tests.js | 8 +- 16 files changed, 613 insertions(+), 121 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index ce453e8ae..a5e3db836 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -417,7 +417,7 @@ export class AbstractType { * @param {AbstractAttributionManager} _am * @return {any} */ - getContent (_am) { + getDelta (_am) { error.methodUnimplemented() } @@ -980,11 +980,6 @@ export const typeMapGetAll = (parent) => { return res } -/** - * @template MapType - * @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent - */ - /** * Render the difference to another ydoc (which can be empty) and highlight the differences with * attributions. @@ -995,16 +990,12 @@ export const typeMapGetAll = (parent) => { * @template MapType * @param {AbstractType} parent * @param {import('../internals.js').AbstractAttributionManager} am - * @return {MapAttributedContent} The Delta representation of this type. * * @private * @function */ -export const typeMapGetContent = (parent, am) => { - /** - * @type {MapAttributedContent} - */ - const mapcontent = {} +export const typeMapGetDelta = (parent, am) => { + const mapdelta = /** @type {delta.MapDeltaBuilder<{ [key:string]: MapType }>} */ (delta.createMapDelta()) parent.doc ?? warnPrematureAccess() parent._map.forEach((item, key) => { /** @@ -1016,7 +1007,7 @@ export const typeMapGetContent = (parent, am) => { const c = array.last(content.getContent()) const attribution = createAttributionFromAttributionItems(attrs, deleted) if (deleted) { - mapcontent[key] = { prevValue: c, value: undefined, attribution } + mapdelta.delete(key, c, attribution) } else { /** * @type {Array>} @@ -1038,10 +1029,10 @@ export const typeMapGetContent = (parent, am) => { } } const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined - mapcontent[key] = { prevValue, value: c, attribution } + mapdelta.set(key, c, prevValue, attribution) } }) - return mapcontent + return mapdelta } /** diff --git a/src/types/YArray.js b/src/types/YArray.js index 5f996755e..5a1cfa42d 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -224,7 +224,7 @@ export class YArray extends AbstractType { * @public */ getContentDeep (am = noAttributionsManager) { - return this.getContent(am).map(d => /** @type {any} */ ( + return this.getDelta(am).map(d => /** @type {any} */ ( d instanceof delta.InsertArrayOp && d.insert instanceof Array ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) : d @@ -243,7 +243,7 @@ export class YArray extends AbstractType { * * @public */ - getContent (am = noAttributionsManager) { + getDelta (am = noAttributionsManager) { return typeListGetContent(this, am) } diff --git a/src/types/YMap.js b/src/types/YMap.js index 7c0e0cbef..fd17252ec 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -13,9 +13,9 @@ import { YMapRefID, callTypeObservers, transact, - typeMapGetContent, + typeMapGetDelta, warnPrematureAccess, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line + MapDelta, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' import * as iterator from 'lib0/iterator' @@ -195,12 +195,12 @@ export class YMap extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {import('../internals.js').AbstractAttributionManager} am - * @return {import('./AbstractType.js').MapAttributedContent} The Delta representation of this type. + * @return {MapDelta<{[key:string]: MapType}>} The Delta representation of this type. * * @public */ - getContent (am) { - return typeMapGetContent(this, am) + getDelta (am) { + return typeMapGetDelta(this, am) } /** diff --git a/src/types/YText.js b/src/types/YText.js index 45e34f747..1e26ede2e 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -789,7 +789,7 @@ export class YText extends AbstractType { * @type {YText} */ const text = new YText() - text.applyDelta(this.getContent()) + text.applyDelta(this.getDelta()) return text } @@ -887,23 +887,13 @@ export class YText extends AbstractType { * @public */ getContentDeep (am = noAttributionsManager) { - return this.getContent(am).map(d => + return this.getDelta(am).map(d => d instanceof delta.InsertEmbedOp && d.insert instanceof AbstractType - ? new delta.InsertEmbedOp(d.insert.getContent(am), d.attributes, d.attribution) + ? new delta.InsertEmbedOp(d.insert.getDelta(am), d.attributes, d.attribution) : d ) } - /** - * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. - * - * @public - */ - getContent (am = noAttributionsManager) { - return this.getDelta(am) - } - /** * Render the difference to another ydoc (which can be empty) and highlight the differences with * attributions. @@ -1301,7 +1291,7 @@ export class YText extends AbstractType { * @param {this} other */ [traits.EqualityTraitSymbol] (other) { - return this.getContent().equals(other.getContent()) + return this.getDelta().equals(other.getDelta()) } } diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 8d3527cd0..833fbfdf7 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -11,7 +11,7 @@ import { typeMapGetAllSnapshot, typeListForEach, YXmlElementRefID, - typeMapGetContent, + typeMapGetDelta, noAttributionsManager, AbstractAttributionManager, Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line } from '../internals.js' @@ -223,7 +223,7 @@ export class YXmlElement extends YXmlFragment { * @public */ getContentDeep (am = noAttributionsManager) { - const { children: origChildren, attributes: origAttributes } = this.getContent(am) + const { children: origChildren, attributes: origAttributes } = this.getDelta(am) const children = origChildren.map(d => /** @type {any} */ ( (d instanceof delta.InsertArrayOp && d.insert instanceof Array) ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) @@ -231,9 +231,9 @@ export class YXmlElement extends YXmlFragment { )) /** * @todo there is a Attributes type and a DeepAttributes type. - * @type {import('./AbstractType.js').MapAttributedContent} + * @type {delta.MapDelta<>} */ - const attributes = {} + const attributes = delta.createMapDelta() object.forEach(origAttributes, (v, key) => { attributes[key] = Object.assign({}, v, { value: v.value instanceof AbstractType ? v.value.getContentDeep(am) : v.value }) }) @@ -251,10 +251,10 @@ export class YXmlElement extends YXmlFragment { * * @public */ - getContent (am = noAttributionsManager) { - const attributes = typeMapGetContent(this, am) - const { children } = super.getContent(am) - return { nodeName: this.nodeName, children, attributes } + getDelta (am = noAttributionsManager) { + const { children } = super.getDelta(am) + const attributes = typeMapGetDelta(this, am) + return new delta.XmlDelta(this.nodeName, children, attributes) } /** diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 804d442c5..e63e0d8ae 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -386,7 +386,7 @@ export class YXmlFragment extends AbstractType { * @param {import('../internals.js').AbstractAttributionManager} am * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} */ - getContent (am = noAttributionsManager) { + getDelta (am = noAttributionsManager) { const children = typeListGetContent(this, am) return { children } } @@ -398,7 +398,7 @@ export class YXmlFragment extends AbstractType { * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} */ getContentDeep (am) { - const { children: origChildren } = this.getContent(am) + const { children: origChildren } = this.getDelta(am) /** * @type {import('../utils/Delta.js').ArrayDelta>} */ diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index 7b5ac37c3..195a1e3d4 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -40,7 +40,7 @@ export class YXmlText extends YText { */ clone () { const text = new YXmlText() - text.applyDelta(this.getContent()) + text.applyDelta(this.getDelta()) return text } @@ -68,7 +68,7 @@ export class YXmlText extends YText { } toString () { - return this.getContent().ops.map(dop => { + return this.getDelta().ops.map(dop => { if (dop instanceof delta.InsertStringOp) { const nestedNodes = [] for (const nodeName in dop.attributes) { diff --git a/src/utils/Delta.js b/src/utils/Delta.js index cf9c54765..f140b693f 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -1,4 +1,5 @@ import * as object from 'lib0/object' +import * as map from 'lib0/map' import * as fun from 'lib0/function' import * as traits from 'lib0/traits' import * as error from 'lib0/error' @@ -6,12 +7,14 @@ import * as error from 'lib0/error' /** * @template {any} ArrayContent * @template {object} Embeds - * @typedef {InsertStringOp|InsertEmbedOp|InsertArrayOp|RetainOp|DeleteOp} DeltaOp + * @template {Delta|undefined} ModifyingDelta + * @typedef {InsertStringOp|InsertEmbedOp|InsertArrayOp|RetainOp|DeleteOp|(ModifyingDelta extends undefined ? never : ModifyOp)} DeltaOp */ /** * @template {object} Embeds - * @typedef {InsertStringOp|InsertEmbedOp|RetainOp|DeleteOp} TextDeltaOp + * @template {Delta|undefined} Modifiers + * @typedef {InsertStringOp|InsertEmbedOp|RetainOp|DeleteOp|(Modifiers extends undefined ? never : ModifyOp)} TextDeltaOp */ /** @@ -32,7 +35,7 @@ import * as error from 'lib0/error' */ /** - * @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution_ } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution_ }} DeltaJsonOp + * @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution_ } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution_ } | { modify: object }} DeltaJsonOp */ export class InsertStringOp { @@ -193,18 +196,71 @@ export class RetainOp { } /** - * @typedef {(TextDelta | ArrayDelta)} Delta + * Delta that can be applied on a YType Embed + * + * @template {Delta} DTypes + */ +export class ModifyOp { + /** + * @param {DTypes} delta + */ + constructor (delta) { + this.modify = delta + } + + get length () { + return 1 + } + + /** + * @return {DeltaJsonOp} + */ + toJSON () { + return { modify: this.modify.toJSON() } + } + + /** + * @param {ModifyOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.modify[traits.EqualityTraitSymbol](other.modify) + } +} + +export class AbstractDelta { + constructor () { + this.remote = false + /** + * @type {any} origin + */ + this.origin = null + this.isDiff = true + } + + /** + * @param {any} _other + */ + [traits.EqualityTraitSymbol] (_other) { + error.methodUnimplemented() + } +} + +/** + * @template {Delta|undefined} [Modifiers=any] + * @typedef {(TextDelta | ArrayDelta | MapDelta | XmlDelta )} Delta */ /** * @template {'array' | 'text' | 'custom'} Type - * @template {DeltaOp} TDeltaOp + * @template {DeltaOp} TDeltaOp + * @template {Delta|undefined} Modifiers */ -export class AbstractDelta { +export class AbstractArrayDelta extends AbstractDelta { /** * @param {Type} type */ constructor (type) { + super() this.type = type /** * @type {Array} @@ -213,12 +269,12 @@ export class AbstractDelta { } /** - * @template {(d:TDeltaOp) => DeltaOp} Mapper + * @template {(d:TDeltaOp) => DeltaOp} Mapper * @param {Mapper} f - * @return {DeltaBuilder infer OP ? OP : unknown>} + * @return {DeltaBuilder infer OP ? OP : unknown,Modifiers>} */ map (f) { - const d = /** @type {DeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) + const d = /** @type {DeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) d.ops = this.ops.map(f) // @ts-ignore d.lastOp = d.ops[d.ops.length - 1] ?? null @@ -226,20 +282,61 @@ export class AbstractDelta { } /** - * @param {(d:TDeltaOp,index:number)=>void} f + * + * Iterate through the changes. There are two approches to iterate through the changes. The + * following examples achieve the same thing: + * + * @example + * d.forEach((op, index) => { + * if (op instanceof delta.InsertArrayOp) { + * op.insert + * } else if (op instanceof delta.RetainOp ) { + * op.retain + * } else if (op instanceof delta.DeleteOp) { + * op.delete + * } + * }) + * + * The second approach doesn't require instanceof checks. + * + * @example + * d.forEach(null, + * (insertOp, index) => insertOp.insert, + * (retainOp, index) => insertOp.retain + * (deleteOp, index) => insertOp.delete + * ) + * + * @param {null|((d:TDeltaOp,index:number)=>void)} f + * @param {null|((insertOp: (InsertEmbedOp | InsertStringOp | InsertArrayOp) & TDeltaOp,index:number)=>void)} insertHandler + * @param {null|((retainOp:RetainOp,index:number)=>void)} retainHandler + * @param {null|((deleteOp:DeleteOp,index:number)=>void)} deleteHandler + * @param {null|(Modifiers extends undefined ? null : ((modifyOp:ModifyOp,index:number)=>void))} modifyHandler */ - forEach (f) { + forEach (f = null, insertHandler = null, retainHandler = null, deleteHandler = null, modifyHandler = null) { for ( let i = 0, index = 0, op = this.ops[i]; i < this.ops.length; i++, index += op.length, op = this.ops[i] ) { - f(op, index) + f?.(op, index) + switch (op.constructor) { + case RetainOp: + retainHandler?.(/** @type {RetainOp} */ (op), index) + break + case DeleteOp: + deleteHandler?.(/** @type {DeleteOp} */ (op), index) + break + case ModifyOp: + modifyHandler?.(/** @type {any}) */ (op), index) + break + default: + insertHandler?.(/** @type {any} */ (op), index) + } } } /** - * @param {AbstractDelta} other + * @param {AbstractArrayDelta} other * @return {boolean} */ equals (other) { @@ -254,13 +351,331 @@ export class AbstractDelta { } /** - * @param {AbstractDelta} other + * @param {AbstractArrayDelta} other */ [traits.EqualityTraitSymbol] (other) { return fun.equalityDeep(this.ops, other.ops) } } +/** + * @template {object} Vals + * @template {keyof Vals} K + * @template {Delta|undefined} Modifiers + * @typedef {(change:MapDeltaChange,key:K) => void} MapDeltaChangeCallback + */ + +/** + * @template V + */ +class MapInsertOp { + /** + * @param {V} value + * @param {V|undefined} prevValue + * @param {Attribution_?} attribution + */ + constructor (value, prevValue, attribution) { + this.prevValue = prevValue + this.attribution = attribution + this.value = value + } + + get type () { return 'insert' } + + toJSON () { + return { + type: this.type, + value: this.value + } + } +} + +/** + * @template V + */ +class MapDeleteOp { + /** + * @param {V|undefined} prevValue + * @param {Attribution_?} attribution + */ + constructor (prevValue, attribution) { + this.prevValue = prevValue + this.attribution = attribution + } + + get value () { return undefined } + + get type () { return 'delete' } + + toJSON () { + return { + type: 'delete' + } + } +} + +/** + * @template {Delta} Modifiers + */ +class MapModifyOp { + /** + * @param {Modifiers} delta + */ + constructor (delta) { + this.modify = delta + } + + get value () { return undefined } + + get type () { return 'insert' } + + toJSON () { + return { + type: 'modify', + modify: this.modify.toJSON() + } + } +} + +/** + * @template V + * @template {Delta|undefined} Modifiers + * @typedef {MapInsertOp | MapDeleteOp | (Modifiers extends undefined ? never : MapModifyOp)} MapDeltaChange + */ + +/** + * @template {object} Vals + * @template {Delta|undefined} [Modifiers=undefined] + */ +export class MapDelta extends AbstractDelta { + constructor () { + super() + /** + * @type {Map>} + */ + this.changes = map.create() + /** + * @type {Attribution_?} + */ + this.usedAttribution = null + } + + /** + * + * Iterate through the changes. There are two approches to iterate through changes. The + * following two examples achieve the same thing: + * + * @example + * d.forEach((op, index) => { + * if (op instanceof delta.InsertArrayOp) { + * op.insert + * } else if (op instanceof delta.RetainOp ) { + * op.retain + * } else if (op instanceof delta.DeleteOp) { + * op.delete + * } else if (op instanceof delta.ModifyOp) { + * op.modify + * } + * }) + * + * The second approach doesn't require instanceof checks. + * + * @example + * d.forEach(null, + * (insertOp, index) => insertOp.insert, + * (retainOp, index) => insertOp.retain + * (deleteOp, index) => insertOp.delete + * (modifyOp, index) => insertOp.modify + * ) + * + * @param {null|((change:MapDeltaChange,key:keyof Vals)=>void)} changeHandler + * @param {null|((insertOp:MapInsertOp,key:keyof Vals)=>void)} insertHandler + * @param {null|((deleteOp:MapDeleteOp,key:keyof Vals)=>void)} deleteHandler + * @param {null|((modifyOp:(MapModifyOp),key:keyof Vals)=>void)} modifyHandler + */ + forEach (changeHandler = null, insertHandler = null, deleteHandler = null, modifyHandler = null) { + this.changes.forEach((change, key) => { + changeHandler?.(change, key) + switch (change.constructor) { + case MapDeleteOp: + deleteHandler?.(/** @type {MapDeleteOp} */ (change), key) + break + case MapInsertOp: + insertHandler?.(/** @type {MapInsertOp} */ (change), key) + break + case MapModifyOp: + modifyHandler?.(/** @type {MapModifyOp} */ (change), key) + break + } + }) + } + + /** + * @template {keyof Vals} K + * + * @param {K} key + * @return {MapDeltaChange | undefined} + */ + get (key) { + return /** @type {MapDeltaChange | undefined} */ (this.changes.get(key)) + } + + /** + * @param {keyof Vals} key + */ + has (key) { + return this.changes.has(key) + } + + /** + * @param {MapDelta} other + * @return {boolean} + */ + equals (other) { + return this[traits.EqualityTraitSymbol](other) + } + + /** + * @return {object} + */ + toJSON () { + /** + * @type {any} + */ + const changes = {} + this.changes.forEach((change, key) => { + changes[key] = change.toJSON() + }) + return changes + } + + /** + * Preferred way to iterate through changes. + * + * @return {IterableIterator<{ [K in keyof Vals]: [K, MapDeltaChange] }[keyof Vals]>} + */ + [Symbol.iterator] () { + // @ts-ignore + return this.changes.entries() + } + + /** + * @param {MapDelta} other + */ + [traits.EqualityTraitSymbol] (other) { + return fun.equalityDeep(this.changes, other.changes) + } + + /** + * @return {MapDelta} + */ + done () { + return this + } +} + +/** + * @template {string|undefined} NodeName + * @template Children + * @template {object} Attrs + * @template {Delta|undefined} [ChildModifiers=undefined] + * @template {Delta|undefined} [AttrModifiers=undefined] + * @template {'done'|'mutable'} [Done='mutable'] + */ +export class XmlDelta extends AbstractDelta { + /** + * @param {NodeName} nodeName + * @param {ArrayDelta} children + * @param {MapDelta} attributes + */ + constructor (nodeName, children = createArrayDelta(), attributes = /** @type {any} */ (createMapDelta())) { + super() + this.nodeName = nodeName + /** + * @type {ArrayDelta} + */ + this.children = children + /** + * @type {Done extends 'mutable' ? MapDeltaBuilder : MapDelta} + */ + this.attributes = /** @type {any} */ (attributes) + } + + toJSON () { + return { + nodeName: this.nodeName, + children: this.children.toJSON(), + attributes: this.attributes.toJSON() + } + } + + /** + * @return {XmlDelta} + */ + done () { + this.children.done() + this.attributes.done() + return /** @type {any} */ (this) + } +} + +/** + * @param {string|undefined} nodeName + */ +export const createXmlDelta = (nodeName = undefined) => new XmlDelta(nodeName) + +/** + * @template {object} Vals + * @template {Delta|undefined} [Modifiers=undefined] + * @extends MapDelta + */ +export class MapDeltaBuilder extends MapDelta { + /** + * @template {keyof Vals} K + * @param {K} key + * @param {Modifiers} delta + */ + modify (key, delta) { + this.changes.set(key, /** @type {any} */ ({ type: 'modify', delta })) + return this + } + + /** + * @template {keyof Vals} K + * @param {K} key + * @param {Vals[K]} newVal + * @param {Vals[K]|undefined} prevValue + * @param {Attribution_?} attribution + */ + set (key, newVal, prevValue = undefined, attribution = null) { + const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) + this.changes.set(key, new MapInsertOp(newVal, prevValue, mergedAttribution)) + return this + } + + /** + * @template {keyof Vals} K + * @param {K} key + * @param {Vals[K]|undefined} prevValue + * @param {Attribution_?} attribution + */ + delete (key, prevValue = undefined, attribution = null) { + const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) + this.changes.set(key, new MapDeleteOp(prevValue, mergedAttribution)) + return this + } + + /** + * @param {Attribution_?} attribution + */ + useAttribution (attribution) { + this.usedAttribution = attribution + return this + } +} + +export const createMapDelta = () => new MapDeltaBuilder() + /** * Helper function to merge attribution and attributes. The latter input "wins". * @@ -272,10 +687,11 @@ const mergeAttrs = (a, b) => object.isEmpty(a) ? b : (object.isEmpty(b) ? a : ob /** * @template {'array' | 'text' | 'custom'} Type - * @template {DeltaOp} TDeltaOp - * @extends AbstractDelta + * @template {DeltaOp} TDeltaOp + * @template {Delta|undefined} Modifiers + * @extends AbstractArrayDelta */ -export class DeltaBuilder extends AbstractDelta { +export class DeltaBuilder extends AbstractArrayDelta { /** * @param {Type} type */ @@ -420,7 +836,8 @@ export class DeltaBuilder extends AbstractDelta { /** * @template {any} ArrayContent - * @extends DeltaBuilder<'array', ArrayDeltaOp>> + * @template {Delta|undefined} Modifiers + * @extends DeltaBuilder<'array', ArrayDeltaOp,Modifiers> */ export class ArrayDelta extends DeltaBuilder { constructor () { @@ -430,7 +847,8 @@ export class ArrayDelta extends DeltaBuilder { /** * @template {object} Embeds - * @extends DeltaBuilder<'text',TextDeltaOp> + * @template {Delta|undefined} [Modifiers=undefined] + * @extends DeltaBuilder<'text',TextDeltaOp,Modifiers> */ export class TextDelta extends DeltaBuilder { constructor () { @@ -440,24 +858,28 @@ export class TextDelta extends DeltaBuilder { /** * @template {'text'|'array'|'custom'} Type - * @template {DeltaOp} DeltaOps - * @typedef {AbstractDelta} DeltaReadonly + * @template {DeltaOp} DeltaOps + * @template {Delta|undefined} Modifiers + * @typedef {AbstractArrayDelta} DeltaReadonly */ /** * @template {object} Embeds - * @typedef {DeltaReadonly<'text',TextDeltaOp>} TextDeltaReadonly + * @template {Delta|undefined} Modifiers + * @typedef {DeltaReadonly<'text',TextDeltaOp,Modifiers>} TextDeltaReadonly */ /** * @template {object} Embeds - * @return {TextDelta} + * @template {Delta|undefined} Modifiers + * @return {TextDelta} */ export const createTextDelta = () => new TextDelta() /** * @template [V=any] - * @return {ArrayDelta} + * @template {Delta|undefined} [Modifiers=undefined] + * @return {ArrayDelta} */ export const createArrayDelta = () => new ArrayDelta() diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 1155818e8..127e7570c 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -41,5 +41,5 @@ export const testTextDecodingCompatibilityV1 = _tc => { const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"291311kk9zjznywohpx"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"xr\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}}] const doc = new Y.Doc() Y.applyUpdate(doc, buffer.fromBase64(oldDoc)) - t.compare(doc.getText('text').getContent().toJSON(), oldVal) + t.compare(doc.getText('text').getDelta().toJSON(), oldVal) } diff --git a/tests/delta.tests.js b/tests/delta.tests.js index d7de0eb77..14df1edfc 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -1,5 +1,6 @@ import * as t from 'lib0/testing' import * as delta from '../src/utils/Delta.js' +import * as Y from 'yjs' /** * @param {t.TestCase} _tc @@ -77,3 +78,91 @@ export const testUseAttribution = _tc => { .done() t.compare(d, d2) } + +/** + * @param {t.TestCase} _tc + */ +export const testMapDelta = _tc => { + const d = /** @type {delta.MapDeltaBuilder<{ key: string, v: number, over: string }>} */ (delta.createMapDelta()) + d.set('key', 'value') + .useAttribution({ delete: ['me'] }) + .delete('v', 94) + .useAttribution(null) + .set('over', 'writeme', 'i existed before') + .set('over', 'andout') + .done() + t.compare(d.toJSON(), { + key: { type: 'insert', value: 'value', prevValue: undefined, attribution: null }, + v: { type: 'delete', value: undefined, prevValue: 94, attribution: { delete: ['me'] } }, + over: { type: 'insert', value: 'andout', prevValue: 'i existed before', attribution: null } + }) + t.compare(d.origin, null) + t.compare(d.remote, false) + t.compare(d.isDiff, true) + d.forEach((change, key) => { + if (key === 'v') { + t.assert(d.get(key)?.prevValue === 94) // should know that value is number + t.assert(change.prevValue === 94) + } else if (key === 'key') { + t.assert(d.get(key)?.value === 'value') // show know that value is a string + t.assert(change.value === 'value') + } else if (key === 'over') { + t.assert(change.value === 'andout') + } else { + throw new Error() + } + }) + for (const [key, change] of d) { + if (key === 'v') { + t.assert(d.get(key)?.prevValue === 94) + t.assert(change.prevValue === 94) // should know that value is number + } else if (key === 'key') { + t.assert(change.value === 'value') // should know that value is number + } else if (key === 'over') { + t.assert(change.value === 'andout') + } else { + throw new Error() + } + } +} + +/** + * @param {t.TestCase} _tc + */ +export const testXmlDelta = _tc => { + const d = /** @type {delta.XmlDelta} */ (delta.createXmlDelta()) + d.children.insert(['hi']) + d.attributes.set('a', 1) + d.attributes.delete('a', 1) + /** + * @type {Array| number>} + */ + const arr = [] + d.children.forEach( + (op, index) => { + if (op instanceof delta.InsertArrayOp) { + arr.push(op.insert, index) + } + }, + (op, index) => { + arr.push(op.insert, index) + }, + (op, index) => { + arr.push(op.retain) + }, + (op, index) => { + arr.push(op.delete) + } + ) + t.compare(arr, [['hi'], 0, ['hi'], 0]) + const x = d.done() + console.log(x) +} + +/** + * @param {t.TestCase} tc + */ +export const testTextModifyingDelta = tc => { + const d = /** @type {delta.TextDelta|Y.Array>} */ (delta.createTextDelta()).insert('hi').insert(new Y.Map()).done() + console.log(d) +} diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 0e833d193..9c71d1d77 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -11,7 +11,7 @@ export const testInconsistentFormat = () => { const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText)) content.format(0, 6, { bold: null }) content.format(6, 4, { type: 'text' }) - t.compare(content.getContent(), delta.fromJSON([ + t.compare(content.getDelta(), delta.fromJSON([ { attributes: { type: 'text' }, insert: 'Merge Test' @@ -94,11 +94,11 @@ export const testUndoText = tc => { t.assert(text0.toString() === 'bcxyz') // test marks text0.format(1, 3, { bold: true }) - t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) + t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) undoManager.undo() - t.compare(text0.getContent(), delta.fromJSON([{ insert: 'bcxyz' }])) + t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'bcxyz' }])) undoManager.redo() - t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) + t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) } /** @@ -694,8 +694,8 @@ export const testUndoDeleteTextFormat = _tc => { }, { insert: ' off the shoulder of Orion.' } ]) - t.compare(text.getContent(), expect) - t.compare(text2.getContent(), expect) + t.compare(text.getDelta(), expect) + t.compare(text2.getDelta(), expect) } /** diff --git a/tests/updates.tests.js b/tests/updates.tests.js index f68c19785..6b3075a9d 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -126,7 +126,7 @@ export const testKeyEncoding = tc => { const update = Y.encodeStateAsUpdateV2(users[0]) Y.applyUpdateV2(users[1], update) - t.compare(text1.getContent().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) + t.compare(text1.getDelta().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) compare(users) } @@ -330,7 +330,7 @@ export const testObfuscateUpdates = _tc => { const omap = odoc.getMap('map') const oarray = odoc.getArray('array') // test ytext - const delta = /** @type {Array} */ (otext.getContent().toJSON()) + const delta = /** @type {Array} */ (otext.getDelta().toJSON()) t.assert(delta.length === 2) t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4) t.assert(object.length(delta[0].attributes) === 1) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 4eaab5d2d..b41d9e0bf 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -529,7 +529,7 @@ export const testAttributedContent = _tc => { yarray.insert(1, [42]) }) const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) - const attributedContent = yarray.getContent(attributionManager) + const attributedContent = yarray.getDelta(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index ff056e9ea..bdfe87045 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -631,21 +631,21 @@ export const testAttributedContent = _tc => { t.group('initial value', () => { ymap.set('test', 42) const expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } } - const attributedContent = ymap.getContent(attributionManager) + const attributedContent = ymap.getDelta(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent) }) t.group('overwrite value', () => { ymap.set('test', 'fourtytwo') const expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } } - const attributedContent = ymap.getContent(attributionManager) + const attributedContent = ymap.getDelta(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent) }) t.group('delete value', () => { ymap.delete('test') const expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } } - const attributedContent = ymap.getContent(attributionManager) + const attributedContent = ymap.getDelta(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent) }) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index f3587e3d5..72b9ec320 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -232,7 +232,7 @@ export const testDeltaBug = _tc => { } ] ytext.applyDelta(addingList) - const result = ytext.getContent() + const result = ytext.getDelta() const expectedResult = delta.createTextDelta() .insert('\n', { 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' }) .insert('\n\n\n', { 'table-col': { width: '150' } }) @@ -1589,7 +1589,7 @@ export const testDeltaBug2 = _tc => { } ] ytext.applyDelta(changeEvent) - const delta = ytext.getContent() + const delta = ytext.getDelta() t.compare(delta.ops[40].toJSON(), { insert: '\n', attributes: { @@ -1670,29 +1670,29 @@ export const testBasicFormat = tc => { }) text0.insert(0, 'abc', { bold: true }) t.assert(text0.toString() === 'abc', 'Basic insert with attributes works') - t.compare(text0.getContent(), delta.createTextDelta().insert('abc', { bold: true }).done()) + t.compare(text0.getDelta(), delta.createTextDelta().insert('abc', { bold: true }).done()) t.compare(eventDelta, delta.createTextDelta().insert('abc', { bold: true })) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)') - t.compare(text0.getContent(), delta.createTextDelta().insert('bc', { bold: true })) + t.compare(text0.getDelta(), delta.createTextDelta().insert('bc', { bold: true })) t.compare(eventDelta, delta.createTextDelta().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(text0.getContent(), delta.createTextDelta().insert('b', { bold: true })) + t.compare(text0.getDelta(), delta.createTextDelta().insert('b', { bold: true })) t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1)) text0.insert(0, 'z', { bold: true }) t.assert(text0.toString() === 'zb') - t.compare(text0.getContent(), delta.createTextDelta().insert('zb', { bold: true })) + t.compare(text0.getDelta(), delta.createTextDelta().insert('zb', { bold: true })) t.compare(eventDelta, delta.createTextDelta().insert('z', { bold: true })) // @ts-ignore t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker') text0.insert(0, 'y') t.assert(text0.toString() === 'yzb') - t.compare(text0.getContent(), delta.createTextDelta().insert('y').insert('zb', { bold: true })) + t.compare(text0.getDelta(), delta.createTextDelta().insert('y').insert('zb', { bold: true })) t.compare(eventDelta, delta.createTextDelta().insert('y')) text0.format(0, 2, { bold: null }) t.assert(text0.toString() === 'yzb') - t.compare(text0.getContent(), delta.createTextDelta().insert('yz').insert('b', { bold: true })) + t.compare(text0.getDelta(), delta.createTextDelta().insert('yz').insert('b', { bold: true })) t.compare(eventDelta, delta.createTextDelta().retain(1).retain(1, { bold: null })) compare(users) } @@ -1707,13 +1707,13 @@ export const testFalsyFormats = tc => { delta = event.delta.toJSON() }) text0.insert(0, 'abcde', { falsy: false }) - t.compare(text0.getContent().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }]) + t.compare(text0.getDelta().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }]) t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }]) text0.format(1, 3, { falsy: true }) - t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(text0.getDelta().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }]) text0.format(2, 1, { falsy: false }) - t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(text0.getDelta().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }]) compare(users) } @@ -1732,7 +1732,7 @@ export const testMultilineFormat = _tc => { { retain: 1 }, // newline character { retain: 10, attributes: { bold: true } } ]) - t.compare(testText.getContent().toJSON(), [ + t.compare(testText.getDelta().toJSON(), [ { insert: 'Test', attributes: { bold: true } }, { insert: '\n' }, { insert: 'Multi-line', attributes: { bold: true } }, @@ -1753,7 +1753,7 @@ export const testNotMergeEmptyLinesFormat = _tc => { { insert: '\nText' }, { insert: '\n', attributes: { title: true } } ]) - t.compare(testText.getContent().toJSON(), [ + t.compare(testText.getDelta().toJSON(), [ { insert: 'Text' }, { insert: '\n', attributes: { title: true } }, { insert: '\nText' }, @@ -1777,7 +1777,7 @@ export const testPreserveAttributesThroughDelete = _tc => { { delete: 1 }, { retain: 1, attributes: { title: true } } ]) - t.compare(testText.getContent().toJSON(), [ + t.compare(testText.getDelta().toJSON(), [ { insert: 'Text' }, { insert: '\n', attributes: { title: true } } ]) @@ -1791,7 +1791,7 @@ export const testGetDeltaWithEmbeds = tc => { text0.applyDelta([{ insert: { linebreak: 's' } }]) - t.compare(text0.getContent().toJSON(), [{ + t.compare(text0.getDelta().toJSON(), [{ insert: { linebreak: 's' } }]) } @@ -1804,7 +1804,7 @@ export const testTypesAsEmbed = tc => { text0.applyDelta([{ insert: new Y.Map([['key', 'val']]) }]) - t.compare(/** @type {delta.InsertEmbedOp} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' }) + t.compare(/** @type {delta.InsertEmbedOp} */ (text0.getDelta().ops[0]).insert.toJSON(), { key: 'val' }) let firedEvent = false text1.observe(event => { const d = event.delta @@ -1813,7 +1813,7 @@ export const testTypesAsEmbed = tc => { firedEvent = true }) testConnector.flushAllMessages() - const delta = text1.getContent().toJSON() + const delta = text1.getDelta().toJSON() t.assert(delta.length === 1) t.compare(/** @type {any} */ (delta[0]).insert.toJSON(), { key: 'val' }) t.assert(firedEvent, 'fired the event observer containing a Type-Embed') @@ -1847,11 +1847,11 @@ export const testSnapshot = tc => { }, { delete: 1 }]) - const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) + const state1 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1)) t.compare(state1.toJSON(), [{ insert: 'abcd' }]) - const state2 = text0.getContent(createAttributionManagerFromSnapshots(snapshot2)) + const state2 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot2)) t.compare(state2.toJSON(), [{ insert: 'axcd' }]) - const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON() + const state2Diff = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON() const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }] t.compare(state2Diff, expected) } @@ -1872,7 +1872,7 @@ export const testSnapshotDeleteAfter = tc => { }, { insert: 'e' }]) - const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) + const state1 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1)) t.compare(state1, delta.fromJSON([{ insert: 'abcd' }])) } @@ -1892,7 +1892,7 @@ export const testToDeltaEmbedAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 }) - const delta0 = text0.getContent().toJSON() + const delta0 = text0.getDelta().toJSON() t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' }, attributes: { width: 100 } }, { insert: 'b', attributes: { bold: true } }]) } @@ -1903,7 +1903,7 @@ export const testToDeltaEmbedNoAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }) - const delta0 = text0.getContent().toJSON() + const delta0 = text0.getDelta().toJSON() t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' } }, { insert: 'b', attributes: { bold: true } }], 'toDelta does not set attributes key when no attributes are present') } @@ -2211,9 +2211,9 @@ export const testFormattingBug = async _tc => { { insert: '\n', attributes: { url: 'http://docs.yjs.dev' } }, { insert: '\n', attributes: { url: 'http://example.com' } } ] - t.compare(text1.getContent().toJSON(), expectedResult) - t.compare(text1.getContent().toJSON(), text2.getContent().toJSON()) - console.log(text1.getContent().toJSON()) + t.compare(text1.getDelta().toJSON(), expectedResult) + t.compare(text1.getDelta().toJSON(), text2.getDelta().toJSON()) + console.log(text1.getDelta().toJSON()) } /** @@ -2241,8 +2241,8 @@ export const testDeleteFormatting = _tc => { { insert: 'on ', attributes: { bold: true } }, { insert: 'fire off the shoulder of Orion.' } ] - t.compare(text.getContent().toJSON(), expected) - t.compare(text2.getContent().toJSON(), expected) + t.compare(text.getDelta().toJSON(), expected) + t.compare(text2.getDelta().toJSON(), expected) } /** @@ -2261,14 +2261,14 @@ export const testAttributedContent = _tc => { t.group('insert / delete / format', () => { ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') - const attributedContent = ytext.getContent(attributionManager) + const attributedContent = ytext.getDelta(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) t.group('unformat', () => { ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') - const attributedContent = ytext.getContent(attributionManager) + const attributedContent = ytext.getDelta(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) @@ -2299,7 +2299,7 @@ export const testAttributedDiffing = _tc => { // implementations is the TwosetAttributionManager const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions) // we render the attributed content with the attributionManager - const attributedContent = ytext.getContent(attributionManager) + const attributedContent = ytext.getDelta(attributionManager) console.log(JSON.stringify(attributedContent.toJSON(), null, 2)) const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') t.assert(attributedContent.equals(expectedContent)) @@ -2583,7 +2583,7 @@ export const testAttributionManagerDefaultPerformance = tc => { }) t.measureTime(`getContent(attributionManager) performance `, () => { for (let i = 0; i < M; i++) { - ytext.getContent() + ytext.getDelta() } }) } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 44a4eb845..9d504cd15 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -207,7 +207,7 @@ export const testFormattingBug = _tc => { { insert: 'C', attributes: { em: {}, strong: {} } } ] yxml.applyDelta(delta) - t.compare(yxml.getContent().toJSON(), delta) + t.compare(yxml.getDelta().toJSON(), delta) } /** @@ -244,10 +244,10 @@ export const testFragmentAttributedContent = _tc => { yfragment.insert(1, [elem3]) }) const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) - const attributedContent = yfragment.getContent(attributionManager) + const attributedContent = yfragment.getDelta(attributionManager) console.log(attributedContent.children.toJSON()) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) + t.compare(elem1.getDelta(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) }) } @@ -273,7 +273,7 @@ export const testElementAttributedContent = _tc => { yelement.setAttribute('key', '42') }) const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) - const attributedContent = yelement.getContent(attributionManager) + const attributedContent = yelement.getDelta(attributionManager) console.log('children', attributedContent.children.toJSON()) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) From e6ab2bbc127a37cdc64916218e1b61409d012fcb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sat, 19 Jul 2025 16:17:05 +0200 Subject: [PATCH 346/362] major update on the (nested) event system. created dedicated delta classes to represent changes and content representations on all types. --- package-lock.json | 2 +- package.json | 2 +- src/types/AbstractType.js | 66 ++++++----- src/types/YArray.js | 27 +++-- src/types/YMap.js | 4 +- src/types/YText.js | 36 +++--- src/types/YXmlElement.js | 27 +++-- src/types/YXmlFragment.js | 10 +- src/types/YXmlText.js | 4 +- src/utils/AttributionManager.js | 31 +++-- src/utils/Delta.js | 195 ++++++++++++++++++++++++-------- src/utils/YEvent.js | 4 +- src/utils/types.js | 11 ++ test.html | 6 +- tests/attribution.tests.js | 6 +- tests/compatibility.tests.js | 2 +- tests/delta.tests.js | 77 +++++++++++-- tests/undo-redo.tests.js | 21 +--- tests/updates.tests.js | 4 +- tests/y-array.tests.js | 5 +- tests/y-map.tests.js | 23 ++-- tests/y-text.tests.js | 79 ++++++------- tests/y-xml.tests.js | 29 ++--- 23 files changed, 426 insertions(+), 245 deletions(-) create mode 100644 src/utils/types.js diff --git a/package-lock.json b/package-lock.json index 7b34ac9b9..d124bcc9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "14.0.0-8", "license": "MIT", "dependencies": { - "lib0": "^0.2.107" + "lib0": "^0.2.114" }, "devDependencies": { "@types/node": "^22.14.1", diff --git a/package.json b/package.json index 309db69c9..20af38087 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.107" + "lib0": "^0.2.114" }, "devDependencies": { "@y/protocols": "^1.0.6-1", diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index a5e3db836..282704b93 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -21,13 +21,6 @@ import * as error from 'lib0/error' import * as math from 'lib0/math' import * as log from 'lib0/logging' -/** - * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution } } }} YXmlDeepContent - */ -/** - * @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution} } }} DeepContent - */ - /** * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats */ @@ -264,8 +257,11 @@ export const callTypeObservers = (type, transaction, event) => { } /** - * @template EventType * Abstract Yjs Type class + * + * @template EventType + * @template {import('../utils/Delta.js').Delta} [EventDelta=any] + * @template {import('../utils/Delta.js').Delta} [EventDeltaDeep=any] */ export class AbstractType { constructor () { @@ -303,10 +299,10 @@ export class AbstractType { } /** - * @return {AbstractType|null} + * @return {AbstractType|null} */ get parent () { - return this._item ? /** @type {AbstractType} */ (this._item.parent) : null + return this._item ? /** @type {AbstractType} */ (this._item.parent) : null } /** @@ -325,7 +321,7 @@ export class AbstractType { } /** - * @return {AbstractType} + * @return {AbstractType} */ _copy () { throw error.methodUnimplemented() @@ -336,7 +332,7 @@ export class AbstractType { * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {AbstractType} + * @return {AbstractType} */ clone () { throw error.methodUnimplemented() @@ -415,15 +411,15 @@ export class AbstractType { /** * @param {AbstractAttributionManager} _am - * @return {any} + * @return {EventDelta} */ - getDelta (_am) { + getContent (_am) { error.methodUnimplemented() } /** * @param {AbstractAttributionManager} _am - * @return {DeepContent} + * @return {EventDeltaDeep} */ getContentDeep (_am) { error.methodUnimplemented() @@ -431,7 +427,7 @@ export class AbstractType { } /** - * @param {AbstractType} type + * @param {AbstractType} type * @param {number} start * @param {number} end * @return {Array} @@ -469,7 +465,7 @@ export const typeListSlice = (type, start, end) => { } /** - * @param {AbstractType} type + * @param {AbstractType} type * @return {Array} * * @private @@ -498,8 +494,10 @@ export const typeListToArray = type => { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @param {AbstractType} type + * @template {delta.ArrayDelta} TypeDelta + * @param {AbstractType} type * @param {import('../internals.js').AbstractAttributionManager} am + * @return {TypeDelta} * * @private * @function @@ -528,11 +526,11 @@ export const typeListGetContent = (type, am) => { } } } - return d + return /** @type {TypeDelta} */ (d.done()) } /** - * @param {AbstractType} type + * @param {AbstractType} type * @param {Snapshot} snapshot * @return {Array} * @@ -557,7 +555,7 @@ export const typeListToArraySnapshot = (type, snapshot) => { /** * Executes a provided function on once on every element of this YArray. * - * @param {AbstractType} type + * @param {AbstractType} type * @param {function(any,number,any):void} f A function to execute on every element of this YArray. * * @private @@ -580,8 +578,8 @@ export const typeListForEach = (type, f) => { /** * @template C,R - * @param {AbstractType} type - * @param {function(C,number,AbstractType):R} f + * @param {AbstractType} type + * @param {function(C,number,AbstractType):R} f * @return {Array} * * @private @@ -599,7 +597,7 @@ export const typeListMap = (type, f) => { } /** - * @param {AbstractType} type + * @param {AbstractType} type * @return {IterableIterator} * * @private @@ -651,8 +649,8 @@ export const typeListCreateIterator = type => { * Executes a provided function on once on every element of this YArray. * Operates on a snapshotted state of the document. * - * @param {AbstractType} type - * @param {function(any,number,AbstractType):void} f A function to execute on every element of this YArray. + * @param {AbstractType} type + * @param {function(any,number,AbstractType):void} f A function to execute on every element of this YArray. * @param {Snapshot} snapshot * * @private @@ -673,7 +671,7 @@ export const typeListForEachSnapshot = (type, f, snapshot) => { } /** - * @param {AbstractType} type + * @param {AbstractType} type * @param {number} index * @return {any} * @@ -700,7 +698,7 @@ export const typeListGet = (type, index) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {AbstractType} parent * @param {Item?} referenceItem * @param {Array|Array|boolean|number|null|string|Uint8Array>} content * @@ -768,7 +766,7 @@ const lengthExceeded = () => error.create('Length exceeded!') /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {AbstractType} parent * @param {number} index * @param {Array|Array|number|null|string|Uint8Array>} content * @@ -821,7 +819,7 @@ export const typeListInsertGenerics = (transaction, parent, index, content) => { * the search marker. * * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {AbstractType} parent * @param {Array|Array|number|null|string|Uint8Array>} content * * @private @@ -841,7 +839,7 @@ export const typeListPushGenerics = (transaction, parent, content) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {AbstractType} parent * @param {number} index * @param {number} length * @@ -888,7 +886,7 @@ export const typeListDelete = (transaction, parent, index, length) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {AbstractType} parent * @param {string} key * * @private @@ -903,9 +901,9 @@ export const typeMapDelete = (transaction, parent, key) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {AbstractType} parent * @param {string} key - * @param {Object|number|null|Array|string|Uint8Array|AbstractType} value + * @param {Object|number|null|Array|string|Uint8Array|AbstractType} value * * @private * @function diff --git a/src/types/YArray.js b/src/types/YArray.js index 5a1cfa42d..54a10a540 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -23,19 +23,28 @@ import { AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' +/** + * + * @template Content + * @template {import('../internals.js').Delta|undefined} Modifiers + * @typedef {import('../internals.js').ArrayDelta} ArrayDelta + */ + import * as delta from '../utils/Delta.js' /** * Event that describes the changes on a YArray - * @template T + * @template {import('../utils/types.js').YValue} T * @extends YEvent> */ export class YArrayEvent extends YEvent {} /** * A shared Array implementation. - * @template T - * @extends AbstractType> + * @template {import('../utils/types.js').YValue} T + * @template {ArrayDelta} [TypeDelta=ArrayDelta] + * @template {T extends AbstractType ? ArrayDelta>|DeepD,DeepD> : ArrayDelta} [EventDeltaDeep=T extends AbstractType ? ArrayDelta>|DeepD,DeepD> : ArrayDelta] + * @extends AbstractType,TypeDelta,EventDeltaDeep> * @implements {Iterable} */ export class YArray extends AbstractType { @@ -54,7 +63,7 @@ export class YArray extends AbstractType { /** * Construct a new YArray containing the specified items. - * @template {Object|Array|number|null|string|Uint8Array} T + * @template {import('../utils/types.js').YValue} T * @param {Array} items * @return {YArray} */ @@ -219,16 +228,16 @@ export class YArray extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').ArrayDelta>} The Delta representation of this type. + * @return {EventDeltaDeep} The Delta representation of this type. * * @public */ getContentDeep (am = noAttributionsManager) { - return this.getDelta(am).map(d => /** @type {any} */ ( + return /** @type {any} */ (this.getContent(am).map(d => /** @type {any} */ ( d instanceof delta.InsertArrayOp && d.insert instanceof Array ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) : d - )) + ))) } /** @@ -239,11 +248,11 @@ export class YArray extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').ArrayDelta>} The Delta representation of this type. + * @return {TypeDelta} The Delta representation of this type. * * @public */ - getDelta (am = noAttributionsManager) { + getContent (am = noAttributionsManager) { return typeListGetContent(this, am) } diff --git a/src/types/YMap.js b/src/types/YMap.js index fd17252ec..7372ba937 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -195,11 +195,11 @@ export class YMap extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {import('../internals.js').AbstractAttributionManager} am - * @return {MapDelta<{[key:string]: MapType}>} The Delta representation of this type. + * @return {MapDelta<{[key:string]: MapType},undefined>} The Delta representation of this type. * * @public */ - getDelta (am) { + getContent (am) { return typeMapGetDelta(this, am) } diff --git a/src/types/YText.js b/src/types/YText.js index 1e26ede2e..c2ba4b377 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -672,12 +672,12 @@ export class YTextEvent extends YEvent { } /** - * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} + * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} */ get changes () { if (this._changes === null) { /** - * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} + * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} */ const changes = { keys: this.keys, @@ -692,20 +692,20 @@ export class YTextEvent extends YEvent { /** * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * * @public */ getDelta (am = noAttributionsManager) { const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - return this.target.getDelta(am, { itemsToRender, retainDeletes: true }) + return this.target.getContent(am, { itemsToRender, retainDeletes: true }) } /** * Compute the changes in the delta format. * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. * - * @type {delta.TextDelta} + * @type {delta.TextDelta} * * @public */ @@ -789,7 +789,7 @@ export class YText extends AbstractType { * @type {YText} */ const text = new YText() - text.applyDelta(this.getDelta()) + text.applyDelta(this.getContent()) return text } @@ -843,7 +843,7 @@ export class YText extends AbstractType { /** * Apply a {@link Delta} on this shared YText type. * - * @param {Array | delta.Delta} delta The changes to apply on this element. + * @param {Array | delta.TextDelta} delta The changes to apply on this element. * @param {AbstractAttributionManager} am * * @public @@ -851,10 +851,7 @@ export class YText extends AbstractType { applyDelta (delta, am = noAttributionsManager) { if (this.doc !== null) { transact(this.doc, transaction => { - /** - * @type {Array} - */ - const deltaOps = /** @type {Array} */ (/** @type {delta.Delta} */ (delta).ops instanceof Array ? /** @type {delta.Delta} */ (delta).ops : delta) + const deltaOps = /** @type {Array} */ (/** @type {delta.TextDelta} */ (delta).ops instanceof Array ? /** @type {delta.TextDelta} */ (delta).ops : delta) const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) for (let i = 0; i < deltaOps.length; i++) { const op = deltaOps[i] @@ -882,14 +879,14 @@ export class YText extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta< Embeds extends import('./AbstractType.js').AbstractType ? import('./AbstractType.js').DeepContent : Embeds >} The Delta representation of this type. + * @return {import('../utils/Delta.js').TextDelta ? SubEvent : Embeds, undefined>} The Delta representation of this type. * * @public */ getContentDeep (am = noAttributionsManager) { - return this.getDelta(am).map(d => + return this.getContent(am).map(d => d instanceof delta.InsertEmbedOp && d.insert instanceof AbstractType - ? new delta.InsertEmbedOp(d.insert.getDelta(am), d.attributes, d.attribution) + ? new delta.InsertEmbedOp(d.insert.getContent(am), d.attributes, d.attribution) : d ) } @@ -906,13 +903,13 @@ export class YText extends AbstractType { * @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender] * @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions * @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. + * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. * * @public */ - getDelta (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false } = {}) { + getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false } = {}) { /** - * @type {import('../utils/Delta.js').TextDelta} + * @type {import('../utils/Delta.js').TextDeltaBuilder} */ const d = delta.createTextDelta() /** @@ -1108,7 +1105,8 @@ export class YText extends AbstractType { } } } - return d.done() + // @todo! fix the typings here + return /** @type {any} */ (d.done()) } /** @@ -1291,7 +1289,7 @@ export class YText extends AbstractType { * @param {this} other */ [traits.EqualityTraitSymbol] (other) { - return this.getDelta().equals(other.getDelta()) + return this.getContent().equals(other.getContent()) } } diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 833fbfdf7..bd61f21f2 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -218,26 +218,33 @@ export class YXmlElement extends YXmlFragment { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {{ nodeName: string, children: delta.ArrayDelta>, attributes: import('./AbstractType.js').MapAttributedContent }} + * @return {{ nodeName: string, children: delta.ArrayDeltaBuilder>, attributes: import('./AbstractType.js').MapAttributedContent }} * * @public */ getContentDeep (am = noAttributionsManager) { - const { children: origChildren, attributes: origAttributes } = this.getDelta(am) + const { children: origChildren, attributes: origAttributes } = this.getContent(am) const children = origChildren.map(d => /** @type {any} */ ( (d instanceof delta.InsertArrayOp && d.insert instanceof Array) - ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) + ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDeltaBuilder>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) : d )) /** * @todo there is a Attributes type and a DeepAttributes type. - * @type {delta.MapDelta<>} + * @type {delta.MapDeltaBuilder} */ const attributes = delta.createMapDelta() - object.forEach(origAttributes, (v, key) => { - attributes[key] = Object.assign({}, v, { value: v.value instanceof AbstractType ? v.value.getContentDeep(am) : v.value }) - }) - return { nodeName: this.nodeName, children, attributes } + origAttributes.forEach( + null, + (insertOp, key) => { + if (insertOp.value instanceof AbstractType) { + attributes.set(key, insertOp.value.getContentDeep(am), null, insertOp.attribution) + } else { + attributes.set(key, insertOp.value, undefined, insertOp.attribution) + } + } + ) + return delta.createXmlDelta(this.nodeName, children, attributes) } /** @@ -251,8 +258,8 @@ export class YXmlElement extends YXmlFragment { * * @public */ - getDelta (am = noAttributionsManager) { - const { children } = super.getDelta(am) + getContent (am = noAttributionsManager) { + const { children } = super.getContent(am) const attributes = typeMapGetDelta(this, am) return new delta.XmlDelta(this.nodeName, children, attributes) } diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index e63e0d8ae..403059b6f 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -384,9 +384,9 @@ export class YXmlFragment extends AbstractType { * Calculate the attributed content using the attribution manager. * * @param {import('../internals.js').AbstractAttributionManager} am - * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} + * @return {{ children: import('../utils/Delta.js').ArrayDeltaBuilderBuilder> }} */ - getDelta (am = noAttributionsManager) { + getContent (am = noAttributionsManager) { const children = typeListGetContent(this, am) return { children } } @@ -395,12 +395,12 @@ export class YXmlFragment extends AbstractType { * Calculate the attributed content using the attribution manager. * * @param {import('../internals.js').AbstractAttributionManager} am - * @return {{ children: import('../utils/Delta.js').ArrayDelta> }} + * @return {{ children: import('../utils/Delta.js').ArrayDeltaBuilderBuilder> }} */ getContentDeep (am) { - const { children: origChildren } = this.getDelta(am) + const { children: origChildren } = this.getContent(am) /** - * @type {import('../utils/Delta.js').ArrayDelta>} + * @type {import('../utils/Delta.js').ArrayDeltaBuilderBuilder>} */ const children = origChildren.map(d => /** @type {any} */ ( d instanceof delta.InsertArrayOp && d.insert instanceof Array diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index 195a1e3d4..7b5ac37c3 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -40,7 +40,7 @@ export class YXmlText extends YText { */ clone () { const text = new YXmlText() - text.applyDelta(this.getDelta()) + text.applyDelta(this.getContent()) return text } @@ -68,7 +68,7 @@ export class YXmlText extends YText { } toString () { - return this.getDelta().ops.map(dop => { + return this.getContent().ops.map(dop => { if (dop instanceof delta.InsertStringOp) { const nestedNodes = [] for (const nodeName in dop.attributes) { diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index a5bb81f22..0dc20073e 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -31,21 +31,21 @@ import { import * as error from 'lib0/error' import { ObservableV2 } from 'lib0/observable' import * as encoding from 'lib0/encoding' +import * as s from 'lib0/schema' + +export const attributionJsonSchema = s.object({ + insert: s.array(s.string).optional, + insertedAt: s.number.optional, + delete: s.array(s.string).optional, + deletedAt: s.number.optional, + attributes: s.record(s.string, s.array(s.string)).optional, + attributedAt: s.number.optional +}) /** * @todo rename this to `insertBy`, `insertAt`, .. * - * @typedef {Object} Attribution - * @property {Array} [Attribution.insert] - * @property {number} [Attribution.insertedAt] - * @property {Array} [Attribution.acceptInsert] - * @property {number} [Attribution.acceptedDeleteAt] - * @property {Array} [Attribution.acceptDelete] - * @property {number} [Attribution.acceptedDeleteAt] - * @property {Array} [Attribution.delete] - * @property {number} [Attribution.deletedAt] - * @property {{ [key: string]: Array }} [Attribution.attributes] - * @property {number} [Attribution.attributedAt] + * @typedef {s.Unwrap} Attribution */ /** @@ -63,18 +63,13 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { */ const attribution = {} if (deleted) { - attribution.delete = [] + attribution.delete = s.array(s.string).ensure([]) } else { attribution.insert = [] } attrs.forEach(attr => { switch (attr.name) { - case 'acceptDelete': - delete attribution.delete - // eslint-disable-next-line no-fallthrough - case 'acceptInsert': - delete attribution.insert - // eslint-disable-next-line no-fallthrough + // eslint-disable-next-line no-fallthrough case 'insert': case 'delete': { const as = /** @type {import('../utils/Delta.js').Attribution_} */ (attribution) diff --git a/src/utils/Delta.js b/src/utils/Delta.js index f140b693f..4f3672461 100644 --- a/src/utils/Delta.js +++ b/src/utils/Delta.js @@ -3,6 +3,8 @@ import * as map from 'lib0/map' import * as fun from 'lib0/function' import * as traits from 'lib0/traits' import * as error from 'lib0/error' +import * as s from 'lib0/schema' +import { attributionJsonSchema } from './AttributionManager.js' /** * @template {any} ArrayContent @@ -50,6 +52,13 @@ export class InsertStringOp { this.attribution = attribution } + /** + * @return {'insert'} + */ + get type () { + return 'insert' + } + get length () { return (this.insert.constructor === Array || this.insert.constructor === String) ? this.insert.length : 1 } @@ -84,6 +93,13 @@ export class InsertArrayOp { this.attribution = attribution } + /** + * @return {'insert'} + */ + get type () { + return 'insert' + } + get length () { return this.insert.length } @@ -118,6 +134,13 @@ export class InsertEmbedOp { this.attribution = attribution } + /** + * @return {'insertEmbed'} + */ + get type () { + return 'insertEmbed' + } + get length () { return 1 } @@ -145,6 +168,13 @@ export class DeleteOp { this.delete = len } + /** + * @return {'delete'} + */ + get type () { + return 'delete' + } + get length () { return 0 } @@ -176,6 +206,13 @@ export class RetainOp { this.attribution = attribution } + /** + * @return {'retain'} + */ + get type () { + return 'retain' + } + get length () { return this.retain } @@ -208,6 +245,13 @@ export class ModifyOp { this.modify = delta } + /** + * @return {'modify'} + */ + get type () { + return 'modify' + } + get length () { return 1 } @@ -247,7 +291,7 @@ export class AbstractDelta { /** * @template {Delta|undefined} [Modifiers=any] - * @typedef {(TextDelta | ArrayDelta | MapDelta | XmlDelta )} Delta + * @typedef {(TextDelta | ArrayDelta | MapDelta | XmlDelta )} Delta */ /** @@ -271,10 +315,10 @@ export class AbstractArrayDelta extends AbstractDelta { /** * @template {(d:TDeltaOp) => DeltaOp} Mapper * @param {Mapper} f - * @return {DeltaBuilder infer OP ? OP : unknown,Modifiers>} + * @return {AbstractArrayDeltaBuilder infer OP ? OP : unknown,Modifiers>} */ map (f) { - const d = /** @type {DeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) + const d = /** @type {AbstractArrayDeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) d.ops = this.ops.map(f) // @ts-ignore d.lastOp = d.ops[d.ops.length - 1] ?? null @@ -307,7 +351,7 @@ export class AbstractArrayDelta extends AbstractDelta { * ) * * @param {null|((d:TDeltaOp,index:number)=>void)} f - * @param {null|((insertOp: (InsertEmbedOp | InsertStringOp | InsertArrayOp) & TDeltaOp,index:number)=>void)} insertHandler + * @param {null|((insertOp:Exclude>,index:number)=>void)} insertHandler * @param {null|((retainOp:RetainOp,index:number)=>void)} retainHandler * @param {null|((deleteOp:DeleteOp,index:number)=>void)} deleteHandler * @param {null|(Modifiers extends undefined ? null : ((modifyOp:ModifyOp,index:number)=>void))} modifyHandler @@ -380,14 +424,26 @@ class MapInsertOp { this.value = value } + /** + * @return {'insert'} + */ get type () { return 'insert' } toJSON () { return { type: this.type, - value: this.value + value: this.value, + prevValue: this.prevValue, + attribution: this.attribution } } + + /** + * @param {MapInsertOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return fun.equalityDeep(this.value, other.value) && fun.equalityDeep(this.prevValue, other.prevValue) && fun.equalityDeep(this.attribution, other.attribution) + } } /** @@ -405,13 +461,25 @@ class MapDeleteOp { get value () { return undefined } + /** + * @type {'delete'} + */ get type () { return 'delete' } toJSON () { return { - type: 'delete' + type: this.type, + prevValue: this.prevValue, + attribution: this.attribution } } + + /** + * @param {MapDeleteOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return fun.equalityDeep(this.prevValue, other.prevValue) && fun.equalityDeep(this.attribution, other.attribution) + } } /** @@ -427,14 +495,24 @@ class MapModifyOp { get value () { return undefined } - get type () { return 'insert' } + /** + * @type {'modify'} + */ + get type () { return 'modify' } toJSON () { return { - type: 'modify', + type: this.type, modify: this.modify.toJSON() } } + + /** + * @param {MapModifyOp} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.modify[traits.EqualityTraitSymbol](other.modify) + } } /** @@ -443,9 +521,17 @@ class MapModifyOp { * @typedef {MapInsertOp | MapDeleteOp | (Modifiers extends undefined ? never : MapModifyOp)} MapDeltaChange */ +export const mapDeltaChangeJsonSchema = s.union( + s.object({ type: s.literal('insert'), value: s.any, prevValue: s.any.optional, attribution: attributionJsonSchema.nullable.optional }), + s.object({ type: s.literal('delete'), prevValue: s.any.optional, attribution: attributionJsonSchema.nullable.optional }), + s.object({ type: s.literal('modify'), modify: s.any }) +) + +export const mapDeltaJsonSchema = s.record(s.string, mapDeltaChangeJsonSchema) + /** * @template {object} Vals - * @template {Delta|undefined} [Modifiers=undefined] + * @template {Delta|undefined} Modifiers */ export class MapDelta extends AbstractDelta { constructor () { @@ -528,7 +614,7 @@ export class MapDelta extends AbstractDelta { } /** - * @param {MapDelta} other + * @param {MapDelta} other * @return {boolean} */ equals (other) { @@ -536,15 +622,15 @@ export class MapDelta extends AbstractDelta { } /** - * @return {object} + * @return {s.Unwrap} */ toJSON () { /** - * @type {any} + * @type {s.Unwrap} */ const changes = {} this.changes.forEach((change, key) => { - changes[key] = change.toJSON() + changes[/** @type {string} */ (key)] = change.toJSON() }) return changes } @@ -560,7 +646,7 @@ export class MapDelta extends AbstractDelta { } /** - * @param {MapDelta} other + * @param {MapDelta} other */ [traits.EqualityTraitSymbol] (other) { return fun.equalityDeep(this.changes, other.changes) @@ -585,18 +671,18 @@ export class MapDelta extends AbstractDelta { export class XmlDelta extends AbstractDelta { /** * @param {NodeName} nodeName - * @param {ArrayDelta} children + * @param {ArrayDeltaBuilder} children * @param {MapDelta} attributes */ - constructor (nodeName, children = createArrayDelta(), attributes = /** @type {any} */ (createMapDelta())) { + constructor (nodeName, children, attributes) { super() this.nodeName = nodeName /** - * @type {ArrayDelta} + * @type {ArrayDeltaBuilder} */ this.children = children /** - * @type {Done extends 'mutable' ? MapDeltaBuilder : MapDelta} + * @type {Done extends 'mutable' ? MapDeltaBuilder : MapDelta} */ this.attributes = /** @type {any} */ (attributes) } @@ -617,12 +703,27 @@ export class XmlDelta extends AbstractDelta { this.attributes.done() return /** @type {any} */ (this) } + + /** + * @param {XmlDelta} other + */ + [traits.EqualityTraitSymbol] (other) { + return this.nodeName === other.nodeName && this.children[traits.EqualityTraitSymbol](other.children) && this.attributes[traits.EqualityTraitSymbol](other.attributes) + } } /** - * @param {string|undefined} nodeName + * @template {string|undefined} NodeName + * @template Children + * @template {object} Attrs + * @template {Delta|undefined} [ChildModifiers=undefined] + * @template {Delta|undefined} [AttrModifiers=undefined] + * @param {NodeName} nodeName + * @param {ArrayDeltaBuilder} children + * @param {MapDeltaBuilder} attributes + * @return {XmlDelta} */ -export const createXmlDelta = (nodeName = undefined) => new XmlDelta(nodeName) +export const createXmlDelta = (nodeName, children = createArrayDelta(), attributes = /** @type {any} */ (createMapDelta())) => new XmlDelta(nodeName, children, attributes) /** * @template {object} Vals @@ -691,7 +792,7 @@ const mergeAttrs = (a, b) => object.isEmpty(a) ? b : (object.isEmpty(b) ? a : ob * @template {Delta|undefined} Modifiers * @extends AbstractArrayDelta */ -export class DeltaBuilder extends AbstractArrayDelta { +export class AbstractArrayDeltaBuilder extends AbstractArrayDelta { /** * @param {Type} type */ @@ -823,72 +924,72 @@ export class DeltaBuilder extends AbstractArrayDelta { } /** - * @return {this} + * @return {Type extends 'array' ? ArrayDelta : (Type extends 'text' ? TextDelta : AbstractArrayDelta)} */ done () { while (this.lastOp != null && this.lastOp instanceof RetainOp && this.lastOp.attributes === null && this.lastOp.attribution === null) { this.ops.pop() this.lastOp = this.ops[this.ops.length - 1] ?? null } - return this + return /** @type {any} */ (this) } } /** * @template {any} ArrayContent * @template {Delta|undefined} Modifiers - * @extends DeltaBuilder<'array', ArrayDeltaOp,Modifiers> + * @extends AbstractArrayDeltaBuilder<'array', ArrayDeltaOp,Modifiers> */ -export class ArrayDelta extends DeltaBuilder { +export class ArrayDeltaBuilder extends AbstractArrayDeltaBuilder { constructor () { super('array') } } /** - * @template {object} Embeds - * @template {Delta|undefined} [Modifiers=undefined] - * @extends DeltaBuilder<'text',TextDeltaOp,Modifiers> + * @template {any} ArrayContent + * @template {Delta|undefined} Modifiers + * @typedef {AbstractArrayDelta<'array', ArrayDeltaOp,Modifiers>} ArrayDelta */ -export class TextDelta extends DeltaBuilder { - constructor () { - super('text') - } -} /** - * @template {'text'|'array'|'custom'} Type - * @template {DeltaOp} DeltaOps + * @template {object} Embeds * @template {Delta|undefined} Modifiers - * @typedef {AbstractArrayDelta} DeltaReadonly + * @typedef {AbstractArrayDelta<'text',TextDeltaOp,Modifiers>} TextDelta */ /** * @template {object} Embeds - * @template {Delta|undefined} Modifiers - * @typedef {DeltaReadonly<'text',TextDeltaOp,Modifiers>} TextDeltaReadonly + * @template {Delta|undefined} [Modifiers=undefined] + * @extends AbstractArrayDeltaBuilder<'text',TextDeltaOp,Modifiers> */ +export class TextDeltaBuilder extends AbstractArrayDeltaBuilder { + constructor () { + super('text') + } +} /** - * @template {object} Embeds - * @template {Delta|undefined} Modifiers - * @return {TextDelta} + * @template {object} [Embeds=any] + * @template {Delta|undefined} [Modifiers=undefined] + * @return {TextDeltaBuilder} */ -export const createTextDelta = () => new TextDelta() +export const createTextDelta = () => new TextDeltaBuilder() /** * @template [V=any] * @template {Delta|undefined} [Modifiers=undefined] - * @return {ArrayDelta} + * @return {ArrayDeltaBuilder} */ -export const createArrayDelta = () => new ArrayDelta() +export const createArrayDelta = () => new ArrayDeltaBuilder() /** + * @template {'custom' | 'text' | 'array'} T * @param {DeltaJson} ops - * @param {'custom' | 'text' | 'array'} type + * @param {T} type */ -export const fromJSON = (ops, type = 'custom') => { - const d = new DeltaBuilder(type) +export const fromJSON = (ops, type) => { + const d = new AbstractArrayDeltaBuilder(type) for (let i = 0; i < ops.length; i++) { const op = /** @type {any} */ (ops[i]) // @ts-ignore diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 3827fb180..37c32eae7 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -1,5 +1,5 @@ import { - TextDelta, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line + TextDeltaBuilder, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line } from '../internals.js' import * as set from 'lib0/set' @@ -42,7 +42,7 @@ export class YEvent { */ this._keys = null /** - * @type {TextDelta?} + * @type {import('./Delta.js').TextDelta?} */ this._delta = null /** diff --git a/src/utils/types.js b/src/utils/types.js new file mode 100644 index 000000000..0901a6b6c --- /dev/null +++ b/src/utils/types.js @@ -0,0 +1,11 @@ + +/** + * @typedef {Object|Array|number|null|string|Uint8Array|BigInt + * |import('../index.js').Array + * |import('../index.js').Map + * |import('../index.js').Text + * |import('../index.js').XmlElement + * |import('../index.js').XmlFragment + * |import('../index.js').XmlText + * |import('../index.js').XmlHook} YValue + */ diff --git a/test.html b/test.html index fb01fd9d7..282340e9c 100644 --- a/test.html +++ b/test.html @@ -42,6 +42,7 @@ "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/patience": "./node_modules/lib0/diff/patience.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", @@ -157,6 +158,7 @@ "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", + "lib0/schema": "./node_modules/lib0/schema.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", @@ -206,6 +208,7 @@ "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/patience": "./node_modules/lib0/diff/patience.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", @@ -320,7 +323,8 @@ "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" + "lib0/performance": "./node_modules/lib0/performance.js", + "lib0/schema": "./node_modules/lib0/schema.js" } } } diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js index 29e21e4d0..13bc896a0 100644 --- a/tests/attribution.tests.js +++ b/tests/attribution.tests.js @@ -39,7 +39,7 @@ export const testAttributedEvents = _tc => { ytext.delete(6, 5) }) const am = Y.createAttributionManagerFromDiff(v1, ydoc) - const c1 = ytext.getDelta(am) + const c1 = ytext.getContent(am) t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) let calledObserver = false ytext.observe(event => { @@ -63,7 +63,7 @@ export const testInsertionsMindingAttributedContent = _tc => { ytext.delete(6, 5) }) const am = Y.createAttributionManagerFromDiff(v1, ydoc) - const c1 = ytext.getDelta(am) + const c1 = ytext.getContent(am) t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) ytext.applyDelta(delta.createTextDelta().retain(11).insert('content'), am) t.assert(ytext.toString() === 'hello content') @@ -81,7 +81,7 @@ export const testInsertionsIntoAttributedContent = _tc => { ytext.insert(6, 'word') }) const am = Y.createAttributionManagerFromDiff(v1, ydoc) - const c1 = ytext.getDelta(am) + const c1 = ytext.getContent(am) t.compare(c1, delta.createTextDelta().insert('hello ').insert('word', null, { insert: [] })) ytext.applyDelta(delta.createTextDelta().retain(9).insert('l'), am) t.assert(ytext.toString() === 'hello world') diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 127e7570c..1155818e8 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -41,5 +41,5 @@ export const testTextDecodingCompatibilityV1 = _tc => { const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"291311kk9zjznywohpx"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"xr\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}}] const doc = new Y.Doc() Y.applyUpdate(doc, buffer.fromBase64(oldDoc)) - t.compare(doc.getText('text').getDelta().toJSON(), oldVal) + t.compare(doc.getText('text').getContent().toJSON(), oldVal) } diff --git a/tests/delta.tests.js b/tests/delta.tests.js index 14df1edfc..0e036e764 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -1,6 +1,7 @@ import * as t from 'lib0/testing' import * as delta from '../src/utils/Delta.js' import * as Y from 'yjs' +import * as schema from 'lib0/schema' /** * @param {t.TestCase} _tc @@ -88,12 +89,11 @@ export const testMapDelta = _tc => { .useAttribution({ delete: ['me'] }) .delete('v', 94) .useAttribution(null) - .set('over', 'writeme', 'i existed before') - .set('over', 'andout') + .set('over', 'andout', 'i existed before') .done() t.compare(d.toJSON(), { key: { type: 'insert', value: 'value', prevValue: undefined, attribution: null }, - v: { type: 'delete', value: undefined, prevValue: 94, attribution: { delete: ['me'] } }, + v: { type: 'delete', prevValue: 94, attribution: { delete: ['me'] } }, over: { type: 'insert', value: 'andout', prevValue: 'i existed before', attribution: null } }) t.compare(d.origin, null) @@ -117,7 +117,7 @@ export const testMapDelta = _tc => { t.assert(d.get(key)?.prevValue === 94) t.assert(change.prevValue === 94) // should know that value is number } else if (key === 'key') { - t.assert(change.value === 'value') // should know that value is number + t.assert(change.value === 'value') // should know that value is string } else if (key === 'over') { t.assert(change.value === 'andout') } else { @@ -147,10 +147,10 @@ export const testXmlDelta = _tc => { (op, index) => { arr.push(op.insert, index) }, - (op, index) => { + (op, _index) => { arr.push(op.retain) }, - (op, index) => { + (op, _index) => { arr.push(op.delete) } ) @@ -159,10 +159,69 @@ export const testXmlDelta = _tc => { console.log(x) } +const textDeltaSchema = schema.object({ + ops: schema.array( + schema.any + ) +}) + /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testTextModifyingDelta = tc => { - const d = /** @type {delta.TextDelta|Y.Array>} */ (delta.createTextDelta()).insert('hi').insert(new Y.Map()).done() +export const testTextModifyingDelta = _tc => { + const d = /** @type {delta.TextDelta|Y.Array,undefined>} */ (delta.createTextDelta().insert('hi').insert(new Y.Map()).done()) + schema.assert(d, textDeltaSchema) console.log(d) } + +/** + * @param {t.TestCase} _tc + */ +export const testYtypeDeltaTypings = _tc => { + const ydoc = new Y.Doc({ gc: false }) + { + const yarray = /** @type {Y.Array} */ (ydoc.getArray('numbers')) + const content = yarray.getContent() + content.forEach( + op => { + schema.union( + schema.constructedBy(delta.InsertArrayOp), + schema.constructedBy(delta.RetainOp), + schema.constructedBy(delta.DeleteOp) + ).ensure(op) + }, + op => { + schema.constructedBy(delta.InsertArrayOp).ensure(op) + }, + op => { + schema.constructedBy(delta.RetainOp).ensure(op) + }, + op => { + schema.constructedBy(delta.DeleteOp).ensure(op) + } + ) + const cdeep = yarray.getContentDeep() + cdeep.forEach( + op => { + schema.union( + schema.constructedBy(delta.InsertArrayOp), + schema.constructedBy(delta.RetainOp), + schema.constructedBy(delta.DeleteOp), + schema.constructedBy(delta.ModifyOp) + ).ensure(op) + }, + op => { + schema.constructedBy(delta.InsertArrayOp).ensure(op) + }, + op => { + schema.constructedBy(delta.RetainOp).ensure(op) + }, + op => { + schema.constructedBy(delta.DeleteOp).ensure(op) + }, + op => { + schema.constructedBy(delta.ModifyOp).ensure(op) + } + ) + } +} diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 9c71d1d77..88812af1e 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -11,16 +11,7 @@ export const testInconsistentFormat = () => { const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText)) content.format(0, 6, { bold: null }) content.format(6, 4, { type: 'text' }) - t.compare(content.getDelta(), delta.fromJSON([ - { - attributes: { type: 'text' }, - insert: 'Merge Test' - }, - { - attributes: { type: 'text', italic: true }, - insert: ' After' - } - ])) + t.compare(content.getContent(), delta.createTextDelta().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true })) } const initializeYDoc = () => { const yDoc = new Y.Doc({ gc: false }) @@ -94,11 +85,11 @@ export const testUndoText = tc => { t.assert(text0.toString() === 'bcxyz') // test marks text0.format(1, 3, { bold: true }) - t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) + t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) undoManager.undo() - t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'bcxyz' }])) + t.compare(text0.getContent(), delta.fromJSON([{ insert: 'bcxyz' }])) undoManager.redo() - t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) + t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) } /** @@ -694,8 +685,8 @@ export const testUndoDeleteTextFormat = _tc => { }, { insert: ' off the shoulder of Orion.' } ]) - t.compare(text.getDelta(), expect) - t.compare(text2.getDelta(), expect) + t.compare(text.getContent(), expect) + t.compare(text2.getContent(), expect) } /** diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 6b3075a9d..f68c19785 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -126,7 +126,7 @@ export const testKeyEncoding = tc => { const update = Y.encodeStateAsUpdateV2(users[0]) Y.applyUpdateV2(users[1], update) - t.compare(text1.getDelta().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) + t.compare(text1.getContent().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) compare(users) } @@ -330,7 +330,7 @@ export const testObfuscateUpdates = _tc => { const omap = odoc.getMap('map') const oarray = odoc.getArray('array') // test ytext - const delta = /** @type {Array} */ (otext.getDelta().toJSON()) + const delta = /** @type {Array} */ (otext.getContent().toJSON()) t.assert(delta.length === 2) t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4) t.assert(object.length(delta[0].attributes) === 1) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index b41d9e0bf..7a31f6321 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -515,6 +515,9 @@ export const testIteratingArrayContainingTypes = _tc => { */ export const testAttributedContent = _tc => { const ydoc = new Y.Doc({ gc: false }) + /** + * @type {Y.Array} + */ const yarray = ydoc.getArray() yarray.insert(0, [1, 2]) let attributionManager = Y.noAttributionsManager @@ -529,7 +532,7 @@ export const testAttributedContent = _tc => { yarray.insert(1, [42]) }) const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) - const attributedContent = yarray.getDelta(attributionManager) + const attributedContent = yarray.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index bdfe87045..db89f5756 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -4,7 +4,8 @@ import { compareIDs, noAttributionsManager, TwosetAttributionManager, - createIdMapFromIdSet + createIdMapFromIdSet, + mapDeltaJsonSchema } from '../src/internals.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' @@ -630,24 +631,24 @@ export const testAttributedContent = _tc => { }) t.group('initial value', () => { ymap.set('test', 42) - const expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } } - const attributedContent = ymap.getDelta(attributionManager) - console.log(attributedContent) - t.compare(expectedContent, attributedContent) + const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'insert', prevValue: undefined, value: 42, attribution: { insert: [] } } }) + const attributedContent = ymap.getContent(attributionManager) + console.log(attributedContent.toJSON()) + t.compare(expectedContent, attributedContent.toJSON()) }) t.group('overwrite value', () => { ymap.set('test', 'fourtytwo') - const expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } } - const attributedContent = ymap.getDelta(attributionManager) + const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'insert', prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }) + const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) - t.compare(expectedContent, attributedContent) + t.compare(expectedContent, attributedContent.toJSON()) }) t.group('delete value', () => { ymap.delete('test') - const expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } } - const attributedContent = ymap.getDelta(attributionManager) + const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'delete', prevValue: 'fourtytwo', attribution: { delete: [] } } }) + const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) - t.compare(expectedContent, attributedContent) + t.compare(expectedContent, attributedContent.toJSON()) }) } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 72b9ec320..db2db6b0f 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -232,7 +232,10 @@ export const testDeltaBug = _tc => { } ] ytext.applyDelta(addingList) - const result = ytext.getDelta() + const result = ytext.getContent() + /** + * @type {delta.TextDelta} + */ const expectedResult = delta.createTextDelta() .insert('\n', { 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' }) .insert('\n\n\n', { 'table-col': { width: '150' } }) @@ -1589,7 +1592,7 @@ export const testDeltaBug2 = _tc => { } ] ytext.applyDelta(changeEvent) - const delta = ytext.getDelta() + const delta = ytext.getContent() t.compare(delta.ops[40].toJSON(), { insert: '\n', attributes: { @@ -1640,21 +1643,21 @@ export const testBasicInsertAndDelete = tc => { text0.insert(0, 'abc') t.assert(text0.toString() === 'abc', 'Basic insert works') - t.compare(eventDelta, delta.fromJSON([{ insert: 'abc' }])) + t.compare(eventDelta, delta.createTextDelta().insert('abc')) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete works (position 0)') - t.compare(eventDelta, delta.fromJSON([{ delete: 1 }])) + t.compare(eventDelta, delta.createTextDelta().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(eventDelta, delta.fromJSON([{ retain: 1 }, { delete: 1 }])) + t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1)) users[0].transact(() => { text0.insert(0, '1') text0.delete(0, 1) }) - t.compare(eventDelta, delta.fromJSON([])) + t.compare(eventDelta, delta.createTextDelta()) compare(users) } @@ -1670,29 +1673,29 @@ export const testBasicFormat = tc => { }) text0.insert(0, 'abc', { bold: true }) t.assert(text0.toString() === 'abc', 'Basic insert with attributes works') - t.compare(text0.getDelta(), delta.createTextDelta().insert('abc', { bold: true }).done()) + t.compare(text0.getContent(), delta.createTextDelta().insert('abc', { bold: true }).done()) t.compare(eventDelta, delta.createTextDelta().insert('abc', { bold: true })) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)') - t.compare(text0.getDelta(), delta.createTextDelta().insert('bc', { bold: true })) + t.compare(text0.getContent(), delta.createTextDelta().insert('bc', { bold: true })) t.compare(eventDelta, delta.createTextDelta().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(text0.getDelta(), delta.createTextDelta().insert('b', { bold: true })) + t.compare(text0.getContent(), delta.createTextDelta().insert('b', { bold: true })) t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1)) text0.insert(0, 'z', { bold: true }) t.assert(text0.toString() === 'zb') - t.compare(text0.getDelta(), delta.createTextDelta().insert('zb', { bold: true })) + t.compare(text0.getContent(), delta.createTextDelta().insert('zb', { bold: true })) t.compare(eventDelta, delta.createTextDelta().insert('z', { bold: true })) // @ts-ignore t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker') text0.insert(0, 'y') t.assert(text0.toString() === 'yzb') - t.compare(text0.getDelta(), delta.createTextDelta().insert('y').insert('zb', { bold: true })) + t.compare(text0.getContent(), delta.createTextDelta().insert('y').insert('zb', { bold: true })) t.compare(eventDelta, delta.createTextDelta().insert('y')) text0.format(0, 2, { bold: null }) t.assert(text0.toString() === 'yzb') - t.compare(text0.getDelta(), delta.createTextDelta().insert('yz').insert('b', { bold: true })) + t.compare(text0.getContent(), delta.createTextDelta().insert('yz').insert('b', { bold: true })) t.compare(eventDelta, delta.createTextDelta().retain(1).retain(1, { bold: null })) compare(users) } @@ -1707,13 +1710,13 @@ export const testFalsyFormats = tc => { delta = event.delta.toJSON() }) text0.insert(0, 'abcde', { falsy: false }) - t.compare(text0.getDelta().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }]) t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }]) text0.format(1, 3, { falsy: true }) - t.compare(text0.getDelta().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }]) text0.format(2, 1, { falsy: false }) - t.compare(text0.getDelta().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }]) compare(users) } @@ -1732,7 +1735,7 @@ export const testMultilineFormat = _tc => { { retain: 1 }, // newline character { retain: 10, attributes: { bold: true } } ]) - t.compare(testText.getDelta().toJSON(), [ + t.compare(testText.getContent().toJSON(), [ { insert: 'Test', attributes: { bold: true } }, { insert: '\n' }, { insert: 'Multi-line', attributes: { bold: true } }, @@ -1753,7 +1756,7 @@ export const testNotMergeEmptyLinesFormat = _tc => { { insert: '\nText' }, { insert: '\n', attributes: { title: true } } ]) - t.compare(testText.getDelta().toJSON(), [ + t.compare(testText.getContent().toJSON(), [ { insert: 'Text' }, { insert: '\n', attributes: { title: true } }, { insert: '\nText' }, @@ -1777,7 +1780,7 @@ export const testPreserveAttributesThroughDelete = _tc => { { delete: 1 }, { retain: 1, attributes: { title: true } } ]) - t.compare(testText.getDelta().toJSON(), [ + t.compare(testText.getContent().toJSON(), [ { insert: 'Text' }, { insert: '\n', attributes: { title: true } } ]) @@ -1791,7 +1794,7 @@ export const testGetDeltaWithEmbeds = tc => { text0.applyDelta([{ insert: { linebreak: 's' } }]) - t.compare(text0.getDelta().toJSON(), [{ + t.compare(text0.getContent().toJSON(), [{ insert: { linebreak: 's' } }]) } @@ -1804,7 +1807,7 @@ export const testTypesAsEmbed = tc => { text0.applyDelta([{ insert: new Y.Map([['key', 'val']]) }]) - t.compare(/** @type {delta.InsertEmbedOp} */ (text0.getDelta().ops[0]).insert.toJSON(), { key: 'val' }) + t.compare(/** @type {delta.InsertEmbedOp} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' }) let firedEvent = false text1.observe(event => { const d = event.delta @@ -1813,7 +1816,7 @@ export const testTypesAsEmbed = tc => { firedEvent = true }) testConnector.flushAllMessages() - const delta = text1.getDelta().toJSON() + const delta = text1.getContent().toJSON() t.assert(delta.length === 1) t.compare(/** @type {any} */ (delta[0]).insert.toJSON(), { key: 'val' }) t.assert(firedEvent, 'fired the event observer containing a Type-Embed') @@ -1847,11 +1850,11 @@ export const testSnapshot = tc => { }, { delete: 1 }]) - const state1 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1)) + const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) t.compare(state1.toJSON(), [{ insert: 'abcd' }]) - const state2 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot2)) + const state2 = text0.getContent(createAttributionManagerFromSnapshots(snapshot2)) t.compare(state2.toJSON(), [{ insert: 'axcd' }]) - const state2Diff = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON() + const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON() const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }] t.compare(state2Diff, expected) } @@ -1872,8 +1875,8 @@ export const testSnapshotDeleteAfter = tc => { }, { insert: 'e' }]) - const state1 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1)) - t.compare(state1, delta.fromJSON([{ insert: 'abcd' }])) + const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) + t.compare(state1, delta.createTextDelta().insert('abcd')) } /** @@ -1892,7 +1895,7 @@ export const testToDeltaEmbedAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 }) - const delta0 = text0.getDelta().toJSON() + const delta0 = text0.getContent().toJSON() t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' }, attributes: { width: 100 } }, { insert: 'b', attributes: { bold: true } }]) } @@ -1903,7 +1906,7 @@ export const testToDeltaEmbedNoAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }) - const delta0 = text0.getDelta().toJSON() + const delta0 = text0.getContent().toJSON() t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' } }, { insert: 'b', attributes: { bold: true } }], 'toDelta does not set attributes key when no attributes are present') } @@ -1944,7 +1947,7 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { }) testConnector.flushAllMessages() /** - * @type {Array>} + * @type {Array>} */ const deltas = [] text0.observe(event => { @@ -2211,9 +2214,9 @@ export const testFormattingBug = async _tc => { { insert: '\n', attributes: { url: 'http://docs.yjs.dev' } }, { insert: '\n', attributes: { url: 'http://example.com' } } ] - t.compare(text1.getDelta().toJSON(), expectedResult) - t.compare(text1.getDelta().toJSON(), text2.getDelta().toJSON()) - console.log(text1.getDelta().toJSON()) + t.compare(text1.getContent().toJSON(), expectedResult) + t.compare(text1.getContent().toJSON(), text2.getContent().toJSON()) + console.log(text1.getContent().toJSON()) } /** @@ -2241,8 +2244,8 @@ export const testDeleteFormatting = _tc => { { insert: 'on ', attributes: { bold: true } }, { insert: 'fire off the shoulder of Orion.' } ] - t.compare(text.getDelta().toJSON(), expected) - t.compare(text2.getDelta().toJSON(), expected) + t.compare(text.getContent().toJSON(), expected) + t.compare(text2.getContent().toJSON(), expected) } /** @@ -2261,14 +2264,14 @@ export const testAttributedContent = _tc => { t.group('insert / delete / format', () => { ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') - const attributedContent = ytext.getDelta(attributionManager) + const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) t.group('unformat', () => { ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') - const attributedContent = ytext.getDelta(attributionManager) + const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) @@ -2299,7 +2302,7 @@ export const testAttributedDiffing = _tc => { // implementations is the TwosetAttributionManager const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions) // we render the attributed content with the attributionManager - const attributedContent = ytext.getDelta(attributionManager) + const attributedContent = ytext.getContent(attributionManager) console.log(JSON.stringify(attributedContent.toJSON(), null, 2)) const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') t.assert(attributedContent.equals(expectedContent)) @@ -2583,7 +2586,7 @@ export const testAttributionManagerDefaultPerformance = tc => { }) t.measureTime(`getContent(attributionManager) performance `, () => { for (let i = 0; i < M; i++) { - ytext.getDelta() + ytext.getContent() } }) } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 9d504cd15..9015f9c44 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -207,7 +207,7 @@ export const testFormattingBug = _tc => { { insert: 'C', attributes: { em: {}, strong: {} } } ] yxml.applyDelta(delta) - t.compare(yxml.getDelta().toJSON(), delta) + t.compare(yxml.getContent().toJSON(), delta) } /** @@ -244,10 +244,10 @@ export const testFragmentAttributedContent = _tc => { yfragment.insert(1, [elem3]) }) const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) - const attributedContent = yfragment.getDelta(attributionManager) + const attributedContent = yfragment.getContent(attributionManager) console.log(attributedContent.children.toJSON()) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(elem1.getDelta(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) + t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) }) } @@ -273,17 +273,17 @@ export const testElementAttributedContent = _tc => { yelement.setAttribute('key', '42') }) const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) - const attributedContent = yelement.getDelta(attributionManager) + const attributedContent = yelement.getContent(attributionManager) console.log('children', attributedContent.children.toJSON()) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { const expectedContent = delta.createArrayDelta().insert( [delta.createTextDelta().insert('hello', null, { delete: [] })], null, { delete: [] } - ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + ).insert([delta.createXmlDelta('span')]) .insert([ delta.createTextDelta().insert('world', null, { insert: [] }) ], null, { insert: [] }) @@ -292,7 +292,8 @@ export const testElementAttributedContent = _tc => { console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.attributes, /** @type {delta.MapDeltaBuilder} */ (delta.createMapDelta()).set('key', '42', undefined, { insert: [] })) + t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.nodeName === 'UNDEFINED') }) }) @@ -321,13 +322,13 @@ export const testElementAttributedContentViaDiffer = _tc => { console.log('attributes', attributedContent.attributes) t.compare(attributedContent.children.toJSON(), expectedContent.toJSON()) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { const expectedContent = delta.createArrayDelta().insert( [delta.createTextDelta().insert('hello')], null, { delete: [] } - ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + ).insert([delta.createXmlDelta('span')]) .insert([ delta.createTextDelta().insert('world', null, { insert: [] }) ], null, { insert: [] }) @@ -336,7 +337,7 @@ export const testElementAttributedContentViaDiffer = _tc => { console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.nodeName === 'UNDEFINED') }) ydoc.transact(() => { @@ -348,7 +349,7 @@ export const testElementAttributedContentViaDiffer = _tc => { [delta.createTextDelta().insert('hello')], null, { delete: [] } - ).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]) + ).insert([delta.createXmlDelta('span')]) .insert([ delta.createTextDelta().insert('bigworld', null, { insert: [] }) ], null, { insert: [] }) @@ -357,13 +358,13 @@ export const testElementAttributedContentViaDiffer = _tc => { console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.nodeName === 'UNDEFINED') }) Y.applyUpdate(ydocV1, Y.encodeStateAsUpdate(ydoc)) t.group('test getContentDeep both docs synced', () => { t.info('expecting diffingAttributionManager to auto update itself') - const expectedContent = delta.createArrayDelta().insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }]).insert([ + const expectedContent = delta.createArrayDelta().insert([delta.createXmlDelta('span')]).insert([ delta.createTextDelta().insert('bigworld') ]) const attributedContent = yelement.getContentDeep(attributionManager) @@ -371,7 +372,7 @@ export const testElementAttributedContentViaDiffer = _tc => { console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.attributes) t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: null } }) + t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: null } }) t.assert(attributedContent.nodeName === 'UNDEFINED') }) } From 5c861336ad409cbb3591bb15941471a099869fae Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Fri, 25 Jul 2025 13:36:39 +0200 Subject: [PATCH 347/362] [readme] add Theneo as a user --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 93adbb70f..3f33d2ea3 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ editing for your IDE or custom editor Collaboratively calculate and convert various data * [ProtonMail | Proton Docs](https://proton.me/drive/docs) - E2E encrypted collaborative documents in Proton Drive. +* [Theneo](https://www.theneo.io/) - AI-powered API docs with live team collaboration. ## Table of Contents From 2f47a98380d361b16f8718b5e21387f4f077eedb Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 15 Sep 2025 18:35:33 +0200 Subject: [PATCH 348/362] add reference to lean-yjs --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 3f33d2ea3..e4ad0beab 100644 --- a/README.md +++ b/README.md @@ -1385,6 +1385,15 @@ But we use state vectors only to describe the state of the local document, so we can compute the missing struct of the remote client. We do not use it to track causality. +### Formal Proof + +[lean-yjs](https://github.com/iasakura/lean-yjs) provides a formal verification +of the YATA CRDT algorithm that Yjs implements, using the Lean theorem prover to +mathematically prove correctness properties. While the CRDT algorithm itself is +correct (currently proven for preservation and commutativity), the project +reveals that [the pseudocode in the original YATA paper contains +errors](https://discuss.yjs.dev/t/lean-yjs-formally-proving-the-yjs-conflict-resolution-algorithms/3875/2). + ## License and Author Yjs and all related projects are [**MIT licensed**](./LICENSE). From 91384b54bf0e202bd9023ca89ed6efdad54bc6c0 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 20 Oct 2025 02:14:02 +0200 Subject: [PATCH 349/362] [wip] refactor for lib0/delta v2 --- README.md | 4 - package-lock.json | 8 +- package.json | 4 +- src/index.js | 5 - src/internals.js | 3 - src/structs/ContentType.js | 12 +- src/structs/Item.js | 31 +- src/types/AbstractType.js | 418 +++++++++++-- src/types/YArray.js | 53 +- src/types/YMap.js | 58 +- src/types/YText.js | 383 +----------- src/types/YXmlElement.js | 125 +--- src/types/YXmlEvent.js | 39 -- src/types/YXmlFragment.js | 229 +------ src/types/YXmlHook.js | 40 +- src/types/YXmlText.js | 78 +-- src/utils/AttributionManager.js | 18 +- src/utils/Delta.js | 1007 ------------------------------- src/utils/Doc.js | 24 +- src/utils/PermanentUserData.js | 140 ----- src/utils/RelativePosition.js | 2 +- src/utils/Transaction.js | 6 +- src/utils/UndoManager.js | 18 +- src/utils/YEvent.js | 149 ++--- src/utils/encoding.js | 3 + src/utils/isParentOf.js | 2 +- src/utils/types.js | 22 +- tests/attribution.tests.js | 14 +- tests/compatibility.tests.js | 4 +- tests/delta.tests.js | 227 ------- tests/encoding.tests.js | 35 +- tests/index.js | 3 +- tests/y-text.tests.js | 143 ++--- tests/y-xml.tests.js | 136 ++--- tsconfig.json | 3 +- 35 files changed, 703 insertions(+), 2743 deletions(-) delete mode 100644 src/types/YXmlEvent.js delete mode 100644 src/utils/Delta.js delete mode 100644 src/utils/PermanentUserData.js delete mode 100644 tests/delta.tests.js diff --git a/README.md b/README.md index e4ad0beab..15c1f2192 100644 --- a/README.md +++ b/README.md @@ -743,8 +743,6 @@ or any of its children.
Clone this type into a fresh Yjs type.
toArray():Array<Y.XmlElement|Y.XmlText>
Copies the children to a new Array.
- toDOM():DocumentFragment -
Transforms this type and all children to new DOM elements.
toString():string
Get the XML serialization of all descendants.
toJSON():string @@ -818,8 +816,6 @@ content and be actually XML compliant.
Clone this type into a fresh Yjs type.
toArray():Array<Y.XmlElement|Y.XmlText>
Copies the children to a new Array.
- toDOM():Element -
Transforms this type and all children to a new DOM element.
toString():string
Get the XML serialization of all descendants.
toJSON():string diff --git a/package-lock.json b/package-lock.json index d124bcc9a..0d6308d90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "rollup": "^4.37.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^5.8.3", + "typescript": "^5.9.3", "yjs": "." }, "engines": { @@ -5292,9 +5292,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 20af38087..c40d18afd 100644 --- a/package.json +++ b/package.json @@ -88,15 +88,15 @@ "lib0": "^0.2.114" }, "devDependencies": { - "@y/protocols": "^1.0.6-1", "@types/node": "^22.14.1", + "@y/protocols": "^1.0.6-1", "concurrently": "^3.6.1", "jsdoc": "^3.6.7", "markdownlint-cli": "^0.41.0", "rollup": "^4.37.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^5.8.3", + "typescript": "^5.9.3", "yjs": "." }, "engines": { diff --git a/src/index.js b/src/index.js index e4c811e2b..03ceba7c9 100644 --- a/src/index.js +++ b/src/index.js @@ -10,10 +10,6 @@ export { YXmlHook as XmlHook, YXmlElement as XmlElement, YXmlFragment as XmlFragment, - YXmlEvent, - YMapEvent, - YArrayEvent, - YTextEvent, YEvent, Item, AbstractStruct, @@ -74,7 +70,6 @@ export { relativePositionToJSON, isParentOf, equalSnapshots, - PermanentUserData, // @TODO experimental tryGc, transact, AbstractConnector, diff --git a/src/internals.js b/src/internals.js index d487ce140..a67f6e83b 100644 --- a/src/internals.js +++ b/src/internals.js @@ -8,7 +8,6 @@ export * from './utils/EventHandler.js' export * from './utils/ID.js' export * from './utils/isParentOf.js' export * from './utils/logging.js' -export * from './utils/PermanentUserData.js' export * from './utils/RelativePosition.js' export * from './utils/Snapshot.js' export * from './utils/StructStore.js' @@ -19,7 +18,6 @@ export * from './utils/YEvent.js' export * from './utils/StructSet.js' export * from './utils/IdMap.js' export * from './utils/AttributionManager.js' -export * from './utils/Delta.js' export * from './types/AbstractType.js' export * from './types/YArray.js' @@ -27,7 +25,6 @@ export * from './types/YMap.js' export * from './types/YText.js' export * from './types/YXmlFragment.js' export * from './types/YXmlElement.js' -export * from './types/YXmlEvent.js' export * from './types/YXmlHook.js' export * from './types/YXmlText.js' diff --git a/src/structs/ContentType.js b/src/structs/ContentType.js index a69677d8a..25ff3f67a 100644 --- a/src/structs/ContentType.js +++ b/src/structs/ContentType.js @@ -6,13 +6,17 @@ import { readYXmlFragment, readYXmlHook, readYXmlText, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item, AbstractType // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Transaction, Item // eslint-disable-line } from '../internals.js' +/** + * @typedef {import('../utils/types.js').YType} YType_CT + */ + import * as error from 'lib0/error' /** - * @type {Array>} + * @type {Array<(decoder: UpdateDecoderV1 | UpdateDecoderV2)=>(import('../utils/types.js').YType)>} * @private */ export const typeRefs = [ @@ -38,11 +42,11 @@ export const YXmlTextRefID = 6 */ export class ContentType { /** - * @param {AbstractType} type + * @param {YType_CT} type */ constructor (type) { /** - * @type {AbstractType} + * @type {YType_CT} */ this.type = type } diff --git a/src/structs/Item.js b/src/structs/Item.js index 0efc8e252..e4070304a 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -29,6 +29,10 @@ import * as error from 'lib0/error' import * as binary from 'lib0/binary' import * as array from 'lib0/array' +/** + * @typedef {import('../utils/types.js').YType} YType__ + */ + /** * @todo This should return several items * @@ -68,7 +72,7 @@ export const followRedone = (store, id) => { export const keepItem = (item, keep) => { while (item !== null && item.keep !== keep) { item.keep = keep - item = /** @type {AbstractType} */ (item.parent)._item + item = /** @type {YType__} */ (item.parent)._item } } @@ -115,7 +119,7 @@ export const splitItem = (transaction, leftItem, diff) => { transaction._mergeStructs.push(rightItem) // update parent._map if (rightItem.parentSub !== null && rightItem.right === null) { - /** @type {AbstractType} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem) + /** @type {YType__} */ (rightItem.parent)._map.set(rightItem.parentSub, rightItem) } } else { rightItem.left = null @@ -173,7 +177,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo if (redone !== null) { return getItemCleanStart(transaction, redone) } - let parentItem = /** @type {AbstractType} */ (item.parent)._item + let parentItem = /** @type {YType__} */ (item.parent)._item /** * @type {Item|null} */ @@ -192,7 +196,10 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo parentItem = getItemCleanStart(transaction, parentItem.redone) } } - const parentType = parentItem === null ? /** @type {AbstractType} */ (item.parent) : /** @type {ContentType} */ (parentItem.content).type + /** + * @type {YType__} + */ + const parentType = /** @type {YType__} */ (parentItem === null ? item.parent : /** @type {ContentType} */ (parentItem.content).type) if (item.parentSub === null) { // Is an array item. Insert at the old position @@ -205,10 +212,10 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo */ let leftTrace = left // trace redone until parent matches - while (leftTrace !== null && /** @type {AbstractType} */ (leftTrace.parent)._item !== parentItem) { + while (leftTrace !== null && /** @type {YType__} */ (leftTrace.parent)._item !== parentItem) { leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone) } - if (leftTrace !== null && /** @type {AbstractType} */ (leftTrace.parent)._item === parentItem) { + if (leftTrace !== null && /** @type {YType__} */ (leftTrace.parent)._item === parentItem) { left = leftTrace break } @@ -220,10 +227,10 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo */ let rightTrace = right // trace redone until parent matches - while (rightTrace !== null && /** @type {AbstractType} */ (rightTrace.parent)._item !== parentItem) { + while (rightTrace !== null && /** @type {YType__} */ (rightTrace.parent)._item !== parentItem) { rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone) } - if (rightTrace !== null && /** @type {AbstractType} */ (rightTrace.parent)._item === parentItem) { + if (rightTrace !== null && /** @type {YType__} */ (rightTrace.parent)._item === parentItem) { right = rightTrace break } @@ -275,7 +282,7 @@ export class Item extends AbstractStruct { * @param {ID | null} origin * @param {Item | null} right * @param {ID | null} rightOrigin - * @param {AbstractType|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it. + * @param {YType__|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it. * @param {string | null} parentSub * @param {AbstractContent} content */ @@ -302,7 +309,7 @@ export class Item extends AbstractStruct { */ this.rightOrigin = rightOrigin /** - * @type {AbstractType|ID|null} + * @type {YType__|ID|null} */ this.parent = parent /** @@ -541,7 +548,7 @@ export class Item extends AbstractStruct { addStruct(transaction.doc.store, this) this.content.integrate(transaction, this) // add parent to transaction.changed - addChangedTypeToTransaction(transaction, /** @type {AbstractType} */ (this.parent), this.parentSub) + addChangedTypeToTransaction(transaction, /** @type {import('../utils/types.js').YType} */ (this.parent), this.parentSub) if ((/** @type {AbstractType} */ (this.parent)._item !== null && /** @type {AbstractType} */ (this.parent)._item.deleted) || (this.parentSub !== null && this.right !== null)) { // delete if parent is deleted or if this is not the current attribute value of parent this.delete(transaction) @@ -635,7 +642,7 @@ export class Item extends AbstractStruct { */ delete (transaction) { if (!this.deleted) { - const parent = /** @type {AbstractType} */ (this.parent) + const parent = /** @type {import('../utils/types.js').YType} */ (this.parent) // adjust the length of parent if (this.countable && this.parentSub === null) { parent._length -= this.length diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 282704b93..bce269c69 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -8,18 +8,32 @@ import { ContentType, createID, ContentAny, + ContentFormat, ContentBinary, + ContentJSON, + ContentDeleted, + ContentString, + ContentEmbed, getItemCleanStart, + noAttributionsManager, ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, // eslint-disable-line } from '../internals.js' -import * as delta from '../utils/Delta.js' +import * as delta from 'lib0/delta' import * as array from 'lib0/array' import * as map from 'lib0/map' import * as iterator from 'lib0/iterator' import * as error from 'lib0/error' import * as math from 'lib0/math' import * as log from 'lib0/logging' +import * as object from 'lib0/object' + +/** + * @typedef {import('../utils/types.js').YType} YType_ + */ +/** + * @typedef {import('../utils/types.js').YValue} _YValue + */ /** * https://docs.yjs.dev/getting-started/working-with-shared-types#caveats @@ -98,7 +112,7 @@ const markPosition = (searchMarker, p, index) => { * * This function always returns a refreshed marker (updated timestamp) * - * @param {AbstractType} yarray + * @param {import('../utils/types.js').YType} yarray * @param {number} index */ export const findMarker = (yarray, index) => { @@ -219,7 +233,7 @@ export const updateMarkerChanges = (searchMarker, index, len) => { /** * Accumulate all (list) children of a type and return them as an Array. * - * @param {AbstractType} t + * @param {AbstractType} t * @return {Array} */ export const getTypeChildren = t => { @@ -237,10 +251,9 @@ export const getTypeChildren = t => { * Call event listeners with an event. This will also add an event to all * parents (for `.observeDeep` handlers). * - * @template EventType - * @param {AbstractType} type + * @param {import('../utils/types.js').YType} type * @param {Transaction} transaction - * @param {EventType} event + * @param {YEvent} event */ export const callTypeObservers = (type, transaction, event) => { const changedType = type @@ -251,17 +264,15 @@ export const callTypeObservers = (type, transaction, event) => { if (type._item === null) { break } - type = /** @type {AbstractType} */ (type._item.parent) + type = /** @type {import('../utils/types.js').YType} */ (type._item.parent) } - callEventHandlerListeners(changedType._eH, event, transaction) + callEventHandlerListeners(/** @type {any} */ (changedType._eH), event, transaction) } /** * Abstract Yjs Type class - * - * @template EventType - * @template {import('../utils/Delta.js').Delta} [EventDelta=any] - * @template {import('../utils/Delta.js').Delta} [EventDeltaDeep=any] + * @template {delta.Delta} [EventDelta=delta.Delta] + * @template {YType_} [Self=any] */ export class AbstractType { constructor () { @@ -284,7 +295,7 @@ export class AbstractType { this._length = 0 /** * Event handlers - * @type {EventHandler} + * @type {EventHandler,Transaction>} */ this._eH = createEventHandler() /** @@ -299,10 +310,18 @@ export class AbstractType { } /** - * @return {AbstractType|null} + * Returns a fresh delta that can be used to change this YType. + * @type {EventDelta} + */ + get change () { + return /** @type {any} */ (delta.create()) + } + + /** + * @return {import('../utils/types.js').YType|null} */ get parent () { - return this._item ? /** @type {AbstractType} */ (this._item.parent) : null + return /** @type {import('../utils/types.js').YType} */ (this._item ? this._item.parent : null) } /** @@ -321,10 +340,11 @@ export class AbstractType { } /** - * @return {AbstractType} + * @return {Self} */ _copy () { - throw error.methodUnimplemented() + // @ts-ignore + return new this.constructor() } /** @@ -332,9 +352,10 @@ export class AbstractType { * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {AbstractType} + * @return {Self} */ clone () { + // @todo remove this method from othern types by doing `_copy().apply(this.getContent())` throw error.methodUnimplemented() } @@ -370,7 +391,7 @@ export class AbstractType { /** * Observe all events that are created on this type. * - * @param {function(EventType, Transaction):void} f Observer function + * @param {(target: YEvent, tr: Transaction) => void} f Observer function */ observe (f) { addEventHandlerListener(this._eH, f) @@ -388,7 +409,7 @@ export class AbstractType { /** * Unregister an observer function. * - * @param {function(EventType,Transaction):void} f Observer function + * @param {(type:YEvent,tr:Transaction)=>void} f Observer function */ unobserve (f) { removeEventHandlerListener(this._eH, f) @@ -410,24 +431,284 @@ export class AbstractType { toJSON () {} /** - * @param {AbstractAttributionManager} _am - * @return {EventDelta} + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the + * attribution `{ isDeleted: true, .. }`. + * + * @param {AbstractAttributionManager} am + * @param {Object} [opts] + * @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender] + * @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions + * @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only + * @param {Set?} [opts.renderAttrs] - if true, retain rendered+attributed deletes only + * @param {boolean} [opts.renderChildren] - if true, retain rendered+attributed deletes only + * @return {EventDelta} The Delta representation of this type. + * + * @public */ - getContent (_am) { - error.methodUnimplemented() + getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true } = {}) { + /** + * @type {EventDelta} + */ + const d = /** @type {any} */ (delta.create()) + typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am) + if (renderChildren) { + /** + * @type {delta.FormattingAttributes} + */ + let currentAttributes = {} // saves all current attributes for insert + let usingCurrentAttributes = false + /** + * @type {delta.FormattingAttributes} + */ + let changedAttributes = {} // saves changed attributes for retain + let usingChangedAttributes = false + /** + * Logic for formatting attribute attribution + * Everything that comes after an formatting attribute is formatted by the user that created it. + * Two exceptions: + * - the user resets formatting to the previously known formatting that is not attributed + * - the user deletes a formatting attribute and hence restores the previously known formatting + * that is not attributed. + * @type {delta.FormattingAttributes} + */ + const previousUnattributedAttributes = {} // contains previously known unattributed formatting + /** + * @type {delta.FormattingAttributes} + */ + const previousAttributes = {} // The value before changes + /** + * @type {Array>} + */ + const cs = [] + for (let item = this._start; item !== null; cs.length = 0) { + if (itemsToRender != null) { + for (; item !== null && cs.length < 50; item = item.right) { + const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length) + let itemContent = rslice.length > 1 ? item.content.copy() : item.content + for (let ir = 0; ir < rslice.length; ir++) { + const idrange = rslice[ir] + const content = itemContent + if (ir !== rslice.length - 1) { + itemContent = itemContent.splice(idrange.len) + } + am.readContent(cs, item.id.client, idrange.clock, item.deleted, content, idrange.exists ? 2 : 0) + } + } + } else { + for (; item !== null && cs.length < 50; item = item.right) { + am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) + } + } + for (let i = 0; i < cs.length; i++) { + const c = cs[i] + // render (attributed) content even if it was deleted + const renderContent = c.render && (!c.deleted || c.attrs != null) + // content that was just deleted. It is not rendered as an insertion, because it doesn't + // have any attributes. + const renderDelete = c.render && c.deleted + // existing content that should be retained, only adding changed attributes + const retainContent = !c.render && (!c.deleted || c.attrs != null) + const attribution = (renderContent || c.content.constructor === ContentFormat) ? createAttributionFromAttributionItems(c.attrs, c.deleted) : null + switch (c.content.constructor) { + case ContentDeleted: { + if (renderDelete) d.delete(c.content.getLength()) + break + } + case ContentString: + if (renderContent) { + d.usedAttributes = currentAttributes + usingCurrentAttributes = true + if (c.deleted ? retainDeletes : retainInserts) { + d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution ?? {}) + } else { + d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) + } + } else if (renderDelete) { + d.delete(c.content.getLength()) + } else if (retainContent) { + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(c.content.getLength()) + } + break + case ContentEmbed: + case ContentAny: + case ContentJSON: + case ContentType: + case ContentBinary: + if (renderContent) { + d.usedAttributes = currentAttributes + usingCurrentAttributes = true + if (c.deleted ? retainDeletes : retainInserts) { + d.retain(c.content.getLength(), null, attribution ?? {}) + } else { + d.insert(c.content.getContent(), null, attribution) + } + } else if (renderDelete) { + d.delete(1) + } else if (retainContent) { + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(1) + } + break + case ContentFormat: { + const { key, value } = /** @type {ContentFormat} */ (c.content) + const currAttrVal = currentAttributes[key] ?? null + if (attribution != null && (c.deleted || !object.hasProperty(previousUnattributedAttributes, key))) { + previousUnattributedAttributes[key] = c.deleted ? value : currAttrVal + } + // @todo write a function "updateCurrentAttributes" and "updateChangedAttributes" + // # Update Attributes + if (renderContent || renderDelete) { + // create fresh references + if (usingCurrentAttributes) { + currentAttributes = object.assign({}, currentAttributes) + usingCurrentAttributes = false + } + if (usingChangedAttributes) { + usingChangedAttributes = false + changedAttributes = object.assign({}, changedAttributes) + } + } + if (renderContent || renderDelete) { + if (c.deleted) { + // content was deleted, but is possibly attributed + if (!equalAttrs(value, currAttrVal)) { // do nothing if nothing changed + if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { + delete changedAttributes[key] + } else { + changedAttributes[key] = currAttrVal + } + // current attributes doesn't change + previousAttributes[key] = value + } + } else { // !c.deleted + // content was inserted, and is possibly attributed + if (equalAttrs(value, currAttrVal)) { + // item.delete(transaction) + } else if (equalAttrs(value, previousAttributes[key] ?? null)) { + delete changedAttributes[key] + } else { + changedAttributes[key] = value + } + if (value == null) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } + } + } else if (retainContent && !c.deleted) { + // fresh reference to currentAttributes only + if (usingCurrentAttributes) { + currentAttributes = object.assign({}, currentAttributes) + usingCurrentAttributes = false + } + if (usingChangedAttributes && changedAttributes[key] !== undefined) { + usingChangedAttributes = false + changedAttributes = object.assign({}, changedAttributes) + } + if (value == null) { + delete currentAttributes[key] + } else { + currentAttributes[key] = value + } + delete changedAttributes[key] + previousAttributes[key] = value + } + // # Update Attributions + if (attribution != null || object.hasProperty(previousUnattributedAttributes, key)) { + /** + * @type {import('../utils/AttributionManager.js').Attribution} + */ + const formattingAttribution = object.assign({}, d.usedAttribution) + const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + if (attribution == null || equalAttrs(previousUnattributedAttributes[key], currentAttributes[key] ?? null)) { + // an unattributed formatting attribute was found or an attributed formatting + // attribute was found that resets to the previous status + delete changedAttributedAttributes[key] + delete previousUnattributedAttributes[key] + } else { + const by = changedAttributedAttributes[key] = (changedAttributedAttributes[key]?.slice() ?? []) + by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt) formattingAttribution.attributedAt = attributedAt + } + if (object.isEmpty(changedAttributedAttributes)) { + d.useAttribution(null) + } else if (attribution != null) { + const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) + if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + d.useAttribution(formattingAttribution) + } + } + break + } + } + } + } + } + return d } /** - * @param {AbstractAttributionManager} _am - * @return {EventDeltaDeep} + * Render the difference to another ydoc (which can be empty) and highlight the differences with + * attributions. + * + * @param {AbstractAttributionManager} am + * @return {ToDeepEventDelta} */ - getContentDeep (_am) { - error.methodUnimplemented() + getContentDeep (am = noAttributionsManager) { + const d = this.getContent(am) + d.children.forEach(op => { + if (op instanceof delta.InsertOp) { + op.insert = /** @type {any} */ (op.insert.map(ins => + ins instanceof AbstractType + // @ts-ignore + ? ins.getContentDeep(am) + : ins) + ) + } + }) + d.attrs.forEach((op) => { + if (delta.$insertOp.check(op) && op.value instanceof AbstractType) { + op.value = op.value.getContentDeep(am) + } + }) + return /** @type {any} */ (d) } } /** - * @param {AbstractType} type + * @param {any} a + * @param {any} b + * @return {boolean} + */ +export const equalAttrs = (a, b) => a === b || (typeof a === 'object' && typeof b === 'object' && a && b && object.equalFlat(a, b)) + +/** + * @template {delta.Delta} D + * @typedef {D extends delta.Delta + * ? delta.Delta< + * N, + * { [K in keyof Attrs]: TypeToDelta }, + * TypeToDelta, + * Text + * > + * : D + * } ToDeepEventDelta + */ + +/** + * @template {any} T + * @typedef {(Extract> extends AbstractType ? (unknown extends D ? never : ToDeepEventDelta) : never) | Exclude>} TypeToDelta + */ + +/** + * @param {AbstractType} type * @param {number} start * @param {number} end * @return {Array} @@ -465,7 +746,7 @@ export const typeListSlice = (type, start, end) => { } /** - * @param {AbstractType} type + * @param {import('../utils/types.js').YType} type * @return {Array} * * @private @@ -488,23 +769,23 @@ export const typeListToArray = type => { } /** + * @todo this can be removed as this can be replaced by a generic function * Render the difference to another ydoc (which can be empty) and highlight the differences with * attributions. * * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @template {delta.ArrayDelta} TypeDelta - * @param {AbstractType} type + * @template {delta.Delta} TypeDelta + * @param {TypeDelta} d + * @param {import('../utils/types.js').YType} type * @param {import('../internals.js').AbstractAttributionManager} am - * @return {TypeDelta} * * @private * @function */ -export const typeListGetContent = (type, am) => { +export const typeListGetContent = (d, type, am) => { type.doc ?? warnPrematureAccess() - const d = delta.createArrayDelta() /** * @type {Array>} */ @@ -526,11 +807,10 @@ export const typeListGetContent = (type, am) => { } } } - return /** @type {TypeDelta} */ (d.done()) } /** - * @param {AbstractType} type + * @param {AbstractType} type * @param {Snapshot} snapshot * @return {Array} * @@ -555,7 +835,7 @@ export const typeListToArraySnapshot = (type, snapshot) => { /** * Executes a provided function on once on every element of this YArray. * - * @param {AbstractType} type + * @param {AbstractType} type * @param {function(any,number,any):void} f A function to execute on every element of this YArray. * * @private @@ -578,8 +858,8 @@ export const typeListForEach = (type, f) => { /** * @template C,R - * @param {AbstractType} type - * @param {function(C,number,AbstractType):R} f + * @param {AbstractType} type + * @param {function(C,number,AbstractType):R} f * @return {Array} * * @private @@ -597,7 +877,7 @@ export const typeListMap = (type, f) => { } /** - * @param {AbstractType} type + * @param {AbstractType} type * @return {IterableIterator} * * @private @@ -649,8 +929,8 @@ export const typeListCreateIterator = type => { * Executes a provided function on once on every element of this YArray. * Operates on a snapshotted state of the document. * - * @param {AbstractType} type - * @param {function(any,number,AbstractType):void} f A function to execute on every element of this YArray. + * @param {AbstractType} type + * @param {function(any,number,AbstractType):void} f A function to execute on every element of this YArray. * @param {Snapshot} snapshot * * @private @@ -671,7 +951,7 @@ export const typeListForEachSnapshot = (type, f, snapshot) => { } /** - * @param {AbstractType} type + * @param {import('../utils/types.js').YType} type * @param {number} index * @return {any} * @@ -698,9 +978,9 @@ export const typeListGet = (type, index) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {YType_} parent * @param {Item?} referenceItem - * @param {Array|Array|boolean|number|null|string|Uint8Array>} content + * @param {Array<_YValue>} content * * @private * @function @@ -750,7 +1030,7 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem, break default: if (c instanceof AbstractType) { - left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(c)) + left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(/** @type {any} */ (c))) left.integrate(transaction, 0) } else { throw new Error('Unexpected content type in insert operation') @@ -766,7 +1046,7 @@ const lengthExceeded = () => error.create('Length exceeded!') /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {YType_} parent * @param {number} index * @param {Array|Array|number|null|string|Uint8Array>} content * @@ -819,7 +1099,7 @@ export const typeListInsertGenerics = (transaction, parent, index, content) => { * the search marker. * * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {YType_} parent * @param {Array|Array|number|null|string|Uint8Array>} content * * @private @@ -839,7 +1119,7 @@ export const typeListPushGenerics = (transaction, parent, content) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {number} index * @param {number} length * @@ -886,7 +1166,7 @@ export const typeListDelete = (transaction, parent, index, length) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {YType_} parent * @param {string} key * * @private @@ -901,9 +1181,9 @@ export const typeMapDelete = (transaction, parent, key) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {YType_} parent * @param {string} key - * @param {Object|number|null|Array|string|Uint8Array|AbstractType} value + * @param {_YValue} value * * @private * @function @@ -934,7 +1214,7 @@ export const typeMapSet = (transaction, parent, key, value) => { break default: if (value instanceof AbstractType) { - content = new ContentType(value) + content = new ContentType(/** @type {any} */ (value)) } else { throw new Error('Unexpected content type') } @@ -944,7 +1224,7 @@ export const typeMapSet = (transaction, parent, key, value) => { } /** - * @param {AbstractType} parent + * @param {YType_} parent * @param {string} key * @return {Object|number|null|Array|string|Uint8Array|AbstractType|undefined} * @@ -985,17 +1265,23 @@ export const typeMapGetAll = (parent) => { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @template MapType - * @param {AbstractType} parent + * @template {delta.Delta} TypeDelta + * @param {TypeDelta} d + * @param {YType_} parent + * @param {Set?} attrsToRender * @param {import('../internals.js').AbstractAttributionManager} am * * @private * @function */ -export const typeMapGetDelta = (parent, am) => { - const mapdelta = /** @type {delta.MapDeltaBuilder<{ [key:string]: MapType }>} */ (delta.createMapDelta()) +export const typeMapGetDelta = (d, parent, attrsToRender, am) => { parent.doc ?? warnPrematureAccess() - parent._map.forEach((item, key) => { + + /** + * @param {Item} item + * @param {string} key + */ + const renderAttrs = (item, key) => { /** * @type {Array>} */ @@ -1005,7 +1291,7 @@ export const typeMapGetDelta = (parent, am) => { const c = array.last(content.getContent()) const attribution = createAttributionFromAttributionItems(attrs, deleted) if (deleted) { - mapdelta.delete(key, c, attribution) + d.unset(key, attribution, c) } else { /** * @type {Array>} @@ -1027,10 +1313,14 @@ export const typeMapGetDelta = (parent, am) => { } } const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined - mapdelta.set(key, c, prevValue, attribution) + d.set(key, c, attribution, prevValue) } - }) - return mapdelta + } + if (attrsToRender == null) { + parent._map.forEach(renderAttrs) + } else { + attrsToRender.forEach(key => renderAttrs(/** @type {Item} */ (parent._map.get(key)), key)) + } } /** diff --git a/src/types/YArray.js b/src/types/YArray.js index 54a10a540..23642c3ba 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -23,28 +23,12 @@ import { AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' -/** - * - * @template Content - * @template {import('../internals.js').Delta|undefined} Modifiers - * @typedef {import('../internals.js').ArrayDelta} ArrayDelta - */ - -import * as delta from '../utils/Delta.js' - -/** - * Event that describes the changes on a YArray - * @template {import('../utils/types.js').YValue} T - * @extends YEvent> - */ -export class YArrayEvent extends YEvent {} +import * as delta from 'lib0/delta' // eslint-disable-line /** * A shared Array implementation. * @template {import('../utils/types.js').YValue} T - * @template {ArrayDelta} [TypeDelta=ArrayDelta] - * @template {T extends AbstractType ? ArrayDelta>|DeepD,DeepD> : ArrayDelta} [EventDeltaDeep=T extends AbstractType ? ArrayDelta>|DeepD,DeepD> : ArrayDelta] - * @extends AbstractType,TypeDelta,EventDeltaDeep> + * @extends {AbstractType,YArray>} * @implements {Iterable} */ export class YArray extends AbstractType { @@ -84,7 +68,7 @@ export class YArray extends AbstractType { * * Observer functions are fired * * @param {Doc} y The Yjs instance - * @param {Item} item + * @param {Item?} item */ _integrate (y, item) { super._integrate(y, item) @@ -92,13 +76,6 @@ export class YArray extends AbstractType { this._prelimContent = null } - /** - * @return {YArray} - */ - _copy () { - return new YArray() - } - /** * Makes a copy of this data type that can be included somewhere else. * @@ -108,11 +85,12 @@ export class YArray extends AbstractType { */ clone () { /** - * @type {YArray} + * @type {this} */ - const arr = new YArray() + const arr = /** @type {this} */ (new YArray()) arr.insert(0, this.toArray().map(el => - el instanceof AbstractType ? /** @type {typeof el} */ (el.clone()) : el + // @ts-ignore + el instanceof AbstractType ? /** @type {any} */ (el.clone()) : el )) return arr } @@ -130,7 +108,7 @@ export class YArray extends AbstractType { */ _callObserver (transaction, parentSubs) { super._callObserver(transaction, parentSubs) - callTypeObservers(this, transaction, new YArrayEvent(this, transaction)) + callTypeObservers(this, transaction, new YEvent(this, transaction, null)) } /** @@ -228,16 +206,12 @@ export class YArray extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {EventDeltaDeep} The Delta representation of this type. + * @return {delta.ArrayDelta>} The Delta representation of this type. * * @public */ getContentDeep (am = noAttributionsManager) { - return /** @type {any} */ (this.getContent(am).map(d => /** @type {any} */ ( - d instanceof delta.InsertArrayOp && d.insert instanceof Array - ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) - : d - ))) + return super.getContentDeep(am) } /** @@ -248,12 +222,14 @@ export class YArray extends AbstractType { * attribution `{ isDeleted: true, .. }`. * * @param {AbstractAttributionManager} am - * @return {TypeDelta} The Delta representation of this type. + * @return {delta.ArrayDelta} The Delta representation of this type. * * @public */ getContent (am = noAttributionsManager) { - return typeListGetContent(this, am) + const d = this.change + typeListGetContent(d, this, am) + return d } /** @@ -316,6 +292,7 @@ export class YArray extends AbstractType { /** * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder + * @return {import('../utils/types.js').YType} * * @private * @function diff --git a/src/types/YMap.js b/src/types/YMap.js index 7372ba937..f9ff292a4 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -13,35 +13,18 @@ import { YMapRefID, callTypeObservers, transact, - typeMapGetDelta, warnPrematureAccess, - MapDelta, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line } from '../internals.js' import * as iterator from 'lib0/iterator' - -/** - * @template T - * @extends YEvent> - * Event that describes the changes on a YMap. - */ -export class YMapEvent extends YEvent { - /** - * @param {YMap} ymap The YArray that changed. - * @param {Transaction} transaction - * @param {Set} subs The keys that changed. - */ - constructor (ymap, transaction, subs) { - super(ymap, transaction) - this.keysChanged = subs - } -} +import * as delta from 'lib0/delta' // eslint-disable-line /** * @template MapType * A shared Map implementation. * - * @extends AbstractType> + * @extends AbstractType> * @implements {Iterable<[string, MapType]>} */ export class YMap extends AbstractType { @@ -72,7 +55,7 @@ export class YMap extends AbstractType { * * Observer functions are fired * * @param {Doc} y The Yjs instance - * @param {Item} item + * @param {Item?} item */ _integrate (y, item) { super._integrate(y, item) @@ -82,25 +65,15 @@ export class YMap extends AbstractType { this._prelimContent = null } - /** - * @return {YMap} - */ - _copy () { - return new YMap() - } - /** * Makes a copy of this data type that can be included somewhere else. * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {YMap} + * @return {this} */ clone () { - /** - * @type {YMap} - */ - const map = new YMap() + const map = this._copy() this.forEach((value, key) => { map.set(key, value instanceof AbstractType ? /** @type {typeof value} */ (value.clone()) : value) }) @@ -114,7 +87,7 @@ export class YMap extends AbstractType { * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. */ _callObserver (transaction, parentSubs) { - callTypeObservers(this, transaction, new YMapEvent(this, transaction, parentSubs)) + callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs)) } /** @@ -187,22 +160,6 @@ export class YMap extends AbstractType { }) } - /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @param {import('../internals.js').AbstractAttributionManager} am - * @return {MapDelta<{[key:string]: MapType},undefined>} The Delta representation of this type. - * - * @public - */ - getContent (am) { - return typeMapGetDelta(this, am) - } - /** * Returns an Iterator of [key, value] pairs * @@ -291,6 +248,7 @@ export class YMap extends AbstractType { /** * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder + * @return {import('../utils/types.js').YType} * * @private * @function diff --git a/src/types/YText.js b/src/types/YText.js index c2ba4b377..19fd305d9 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -25,27 +25,15 @@ import { ContentType, warnPrematureAccess, noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, Transaction, // eslint-disable-line - createAttributionFromAttributionItems, - mergeIdSets, - diffIdSet, createIdSet, - ContentDeleted + equalAttrs } from '../internals.js' -import * as delta from '../utils/Delta.js' - import * as math from 'lib0/math' import * as traits from 'lib0/traits' -import * as object from 'lib0/object' import * as map from 'lib0/map' import * as error from 'lib0/error' - -/** - * @param {any} a - * @param {any} b - * @return {boolean} - */ -const equalAttrs = (a, b) => a === b || (typeof a === 'object' && typeof b === 'object' && a && b && object.equalFlat(a, b)) +import * as delta from 'lib0/delta' export class ItemTextListPosition { /** @@ -86,7 +74,7 @@ export class ItemTextListPosition { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {number} length * @param {Object} attributes * @@ -214,7 +202,7 @@ const findNextPosition = (transaction, pos, count) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {number} index * @param {boolean} useSearchMarker * @return {ItemTextListPosition} @@ -238,7 +226,7 @@ const findPosition = (transaction, parent, index, useSearchMarker) => { * Negate applied formats * * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {ItemTextListPosition} currPos * @param {Map} negatedAttributes * @@ -311,7 +299,7 @@ const minimizeAttributeChanges = (currPos, attributes) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {ItemTextListPosition} currPos * @param {Object} attributes * @return {Map} @@ -341,9 +329,9 @@ const insertAttributes = (transaction, parent, currPos, attributes) => { /** * @param {Transaction} transaction - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {ItemTextListPosition} currPos - * @param {string|object|AbstractType} text + * @param {string|object|import('../utils/types.js').YType} text * @param {Object} attributes * * @private @@ -638,82 +626,6 @@ const deleteText = (transaction, currPos, length) => { * @typedef {Object} TextAttributes */ -/** - * @template {{ [key:string]: any } | AbstractType } TextEmbeds - * @extends YEvent> - * Event that describes the changes on a YText type. - */ -export class YTextEvent extends YEvent { - /** - * @param {YText} ytext - * @param {Transaction} transaction - * @param {Set} subs The keys that changed - */ - constructor (ytext, transaction, subs) { - super(ytext, transaction) - /** - * Whether the children changed. - * @type {Boolean} - * @private - */ - this.childListChanged = false - /** - * Set of all changed attributes. - * @type {Set} - */ - this.keysChanged = new Set() - subs.forEach((sub) => { - if (sub === null) { - this.childListChanged = true - } else { - this.keysChanged.add(sub) - } - }) - } - - /** - * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} - */ - get changes () { - if (this._changes === null) { - /** - * @type {{added:Set,deleted:Set,keys:Map,delta:delta.TextDelta}} - */ - const changes = { - keys: this.keys, - delta: this.delta, - added: new Set(), - deleted: new Set() - } - this._changes = changes - } - return /** @type {any} */ (this._changes) - } - - /** - * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. - * - * @public - */ - getDelta (am = noAttributionsManager) { - const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - return this.target.getContent(am, { itemsToRender, retainDeletes: true }) - } - - /** - * Compute the changes in the delta format. - * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. - * - * @type {delta.TextDelta} - * - * @public - */ - get delta () { - return this._delta ?? (this._delta = this.getDelta()) - } -} - /** * Type that represents text with formatting information. * @@ -721,8 +633,8 @@ export class YTextEvent extends YEvent { * block formats (format information on a paragraph), embeds (complex elements * like pictures and videos), and text formats (**bold**, *italic*). * - * @template {{ [key:string]:any } | AbstractType} [Embeds={ [key:string]:any } | AbstractType] - * @extends AbstractType> + * @template {{ [key:string]:any } | import('../utils/types.js').YType} [Embeds={ [key:string]:any } | import('../utils/types.js').YType] + * @extends {AbstractType>} */ export class YText extends AbstractType { /** @@ -758,7 +670,7 @@ export class YText extends AbstractType { /** * @param {Doc} y - * @param {Item} item + * @param {Item?} item */ _integrate (y, item) { super._integrate(y, item) @@ -770,13 +682,6 @@ export class YText extends AbstractType { this._pending = null } - /** - * @return {YText} - */ - _copy () { - return new YText() - } - /** * Makes a copy of this data type that can be included somewhere else. * @@ -788,7 +693,7 @@ export class YText extends AbstractType { /** * @type {YText} */ - const text = new YText() + const text = /** @type {any} */ (new YText()) text.applyDelta(this.getContent()) return text } @@ -801,8 +706,8 @@ export class YText extends AbstractType { */ _callObserver (transaction, parentSubs) { super._callObserver(transaction, parentSubs) - const event = new YTextEvent(this, transaction, parentSubs) - callTypeObservers(this, transaction, event) + const event = new YEvent(/** @type {YText} */ (this), transaction, parentSubs) + callTypeObservers(/** @type {YText} */ (this), transaction, event) // If a remote change happened, we try to cleanup potential formatting duplicates. if (!transaction.local && this._hasFormatting) { transaction._needFormattingCleanup = true @@ -843,270 +748,30 @@ export class YText extends AbstractType { /** * Apply a {@link Delta} on this shared YText type. * - * @param {Array | delta.TextDelta} delta The changes to apply on this element. + * @param {delta.TextDelta} d The changes to apply on this element. * @param {AbstractAttributionManager} am * * @public */ - applyDelta (delta, am = noAttributionsManager) { + applyDelta (d, am = noAttributionsManager) { if (this.doc !== null) { transact(this.doc, transaction => { - const deltaOps = /** @type {Array} */ (/** @type {delta.TextDelta} */ (delta).ops instanceof Array ? /** @type {delta.TextDelta} */ (delta).ops : delta) const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) - for (let i = 0; i < deltaOps.length; i++) { - const op = deltaOps[i] - if (op.insert !== undefined) { + for (const op of d.children) { + if (delta.$insertOp.check(op)) { if (op.insert.length > 0 || typeof op.insert !== 'string') { - insertText(transaction, this, currPos, op.insert, op.attributes || {}) + insertText(transaction, this, currPos, op.insert, op.format || {}) } - } else if (op.retain !== undefined) { - currPos.formatText(transaction, this, op.retain, op.attributes || {}) - } else if (op.delete !== undefined) { + } else if (delta.$retainOp.check(op)) { + currPos.formatText(transaction, this, op.retain, op.format || {}) + } else if (delta.$deleteOp.check(op)) { deleteText(transaction, currPos, op.delete) } } }) } else { - /** @type {Array} */ (this._pending).push(() => this.applyDelta(delta)) - } - } - - /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @param {AbstractAttributionManager} am - * @return {import('../utils/Delta.js').TextDelta ? SubEvent : Embeds, undefined>} The Delta representation of this type. - * - * @public - */ - getContentDeep (am = noAttributionsManager) { - return this.getContent(am).map(d => - d instanceof delta.InsertEmbedOp && d.insert instanceof AbstractType - ? new delta.InsertEmbedOp(d.insert.getContent(am), d.attributes, d.attribution) - : d - ) - } - - /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @param {AbstractAttributionManager} am - * @param {Object} [opts] - * @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender] - * @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions - * @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only - * @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type. - * - * @public - */ - getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false } = {}) { - /** - * @type {import('../utils/Delta.js').TextDeltaBuilder} - */ - const d = delta.createTextDelta() - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - let currentAttributes = {} // saves all current attributes for insert - let usingCurrentAttributes = false - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - let changedAttributes = {} // saves changed attributes for retain - let usingChangedAttributes = false - /** - * Logic for formatting attribute attribution - * Everything that comes after an formatting attribute is formatted by the user that created it. - * Two exceptions: - * - the user resets formatting to the previously known formatting that is not attributed - * - the user deletes a formatting attribute and hence restores the previously known formatting - * that is not attributed. - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - const previousUnattributedAttributes = {} // contains previously known unattributed formatting - /** - * @type {import('../utils/Delta.js').FormattingAttributes} - */ - const previousAttributes = {} // The value before changes - - /** - * @type {Array>} - */ - const cs = [] - for (let item = this._start; item !== null; cs.length = 0) { - if (itemsToRender != null) { - for (; item !== null && cs.length < 50; item = item.right) { - const rslice = itemsToRender.slice(item.id.client, item.id.clock, item.length) - let itemContent = rslice.length > 1 ? item.content.copy() : item.content - for (let ir = 0; ir < rslice.length; ir++) { - const idrange = rslice[ir] - const content = itemContent - if (ir !== rslice.length - 1) { - itemContent = itemContent.splice(idrange.len) - } - am.readContent(cs, item.id.client, idrange.clock, item.deleted, content, idrange.exists ? 2 : 0) - } - } - } else { - for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) - } - } - for (let i = 0; i < cs.length; i++) { - const c = cs[i] - // render (attributed) content even if it was deleted - const renderContent = c.render && (!c.deleted || c.attrs != null) - // content that was just deleted. It is not rendered as an insertion, because it doesn't - // have any attributes. - const renderDelete = c.render && c.deleted - // existing content that should be retained, only adding changed attributes - const retainContent = !c.render && (!c.deleted || c.attrs != null) - const attribution = (renderContent || c.content.constructor === ContentFormat) ? createAttributionFromAttributionItems(c.attrs, c.deleted) : null - switch (c.content.constructor) { - case ContentDeleted: { - if (renderDelete) d.delete(c.content.getLength()) - break - } - case ContentType: - case ContentEmbed: - if (renderContent) { - d.usedAttributes = currentAttributes - usingCurrentAttributes = true - if (c.deleted ? retainDeletes : retainInserts) { - d.retain(c.content.getLength(), null, attribution ?? {}) - } else { - d.insert(c.content.getContent()[0], null, attribution) - } - } else if (renderDelete) { - d.delete(1) - } else if (retainContent) { - d.usedAttributes = changedAttributes - usingChangedAttributes = true - d.retain(1) - } - break - case ContentString: - if (renderContent) { - d.usedAttributes = currentAttributes - usingCurrentAttributes = true - if (c.deleted ? retainDeletes : retainInserts) { - d.retain(/** @type {ContentString} */ (c.content).str.length, null, attribution ?? {}) - } else { - d.insert(/** @type {ContentString} */ (c.content).str, null, attribution) - } - } else if (renderDelete) { - d.delete(c.content.getLength()) - } else if (retainContent) { - d.usedAttributes = changedAttributes - usingChangedAttributes = true - d.retain(c.content.getLength()) - } - break - case ContentFormat: { - const { key, value } = /** @type {ContentFormat} */ (c.content) - const currAttrVal = currentAttributes[key] ?? null - if (attribution != null && (c.deleted || !object.hasProperty(previousUnattributedAttributes, key))) { - previousUnattributedAttributes[key] = c.deleted ? value : currAttrVal - } - // @todo write a function "updateCurrentAttributes" and "updateChangedAttributes" - // # Update Attributes - if (renderContent || renderDelete) { - // create fresh references - if (usingCurrentAttributes) { - currentAttributes = object.assign({}, currentAttributes) - usingCurrentAttributes = false - } - if (usingChangedAttributes) { - usingChangedAttributes = false - changedAttributes = object.assign({}, changedAttributes) - } - } - if (renderContent || renderDelete) { - if (c.deleted) { - // content was deleted, but is possibly attributed - if (!equalAttrs(value, currAttrVal)) { // do nothing if nothing changed - if (equalAttrs(currAttrVal, previousAttributes[key] ?? null) && changedAttributes[key] !== undefined) { - delete changedAttributes[key] - } else { - changedAttributes[key] = currAttrVal - } - // current attributes doesn't change - previousAttributes[key] = value - } - } else { // !c.deleted - // content was inserted, and is possibly attributed - if (equalAttrs(value, currAttrVal)) { - // item.delete(transaction) - } else if (equalAttrs(value, previousAttributes[key] ?? null)) { - delete changedAttributes[key] - } else { - changedAttributes[key] = value - } - if (value == null) { - delete currentAttributes[key] - } else { - currentAttributes[key] = value - } - } - } else if (retainContent && !c.deleted) { - // fresh reference to currentAttributes only - if (usingCurrentAttributes) { - currentAttributes = object.assign({}, currentAttributes) - usingCurrentAttributes = false - } - if (usingChangedAttributes && changedAttributes[key] !== undefined) { - usingChangedAttributes = false - changedAttributes = object.assign({}, changedAttributes) - } - if (value == null) { - delete currentAttributes[key] - } else { - currentAttributes[key] = value - } - delete changedAttributes[key] - previousAttributes[key] = value - } - // # Update Attributions - if (attribution != null || object.hasProperty(previousUnattributedAttributes, key)) { - /** - * @type {import('../utils/AttributionManager.js').Attribution} - */ - const formattingAttribution = object.assign({}, d.usedAttribution) - const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) - if (attribution == null || equalAttrs(previousUnattributedAttributes[key], currentAttributes[key] ?? null)) { - // an unattributed formatting attribute was found or an attributed formatting - // attribute was found that resets to the previous status - delete changedAttributedAttributes[key] - delete previousUnattributedAttributes[key] - } else { - const by = changedAttributedAttributes[key] = (changedAttributedAttributes[key]?.slice() ?? []) - by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) - const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt) formattingAttribution.attributedAt = attributedAt - } - if (object.isEmpty(changedAttributedAttributes)) { - d.useAttribution(null) - } else if (attribution != null) { - const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt != null) formattingAttribution.attributedAt = attributedAt - d.useAttribution(formattingAttribution) - } - } - break - } - } - } + /** @type {Array} */ (this._pending).push(() => this.applyDelta(d)) } - // @todo! fix the typings here - return /** @type {any} */ (d.done()) } /** @@ -1295,7 +960,7 @@ export class YText extends AbstractType { /** * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder - * @return {YText} + * @return {import('../utils/types.js').YType} * * @private * @function diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index bd61f21f2..24e2ffd3e 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -9,15 +9,10 @@ import { typeMapGet, typeMapGetAll, typeMapGetAllSnapshot, - typeListForEach, YXmlElementRefID, - typeMapGetDelta, - noAttributionsManager, - AbstractAttributionManager, Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line + Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line } from '../internals.js' -import * as delta from '../utils/Delta.js' - /** * @typedef {Object|number|null|Array|string|Uint8Array|AbstractType} ValueTypes */ @@ -29,7 +24,9 @@ import * as delta from '../utils/Delta.js' * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement * - * @template {{ [key: string]: ValueTypes }} [KV={ [key: string]: string }] + * @template {{ [key: string]: ValueTypes }} [Attrs={ [key: string]: string }] + * @template {any} [Children=any] + * @extends YXmlFragment */ export class YXmlElement extends YXmlFragment { constructor (nodeName = 'UNDEFINED') { @@ -65,7 +62,7 @@ export class YXmlElement extends YXmlFragment { * * Observer functions are fired * * @param {Doc} y The Yjs instance - * @param {Item} item + * @param {Item?} item */ _integrate (y, item) { super._integrate(y, item) @@ -78,10 +75,10 @@ export class YXmlElement extends YXmlFragment { /** * Creates an Item with the same effect as this Item (without position effect) * - * @return {YXmlElement} + * @return {this} */ _copy () { - return new YXmlElement(this.nodeName) + return /** @type {any} */ (new YXmlElement(this.nodeName)) } /** @@ -89,13 +86,10 @@ export class YXmlElement extends YXmlFragment { * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {YXmlElement} + * @return {this} */ clone () { - /** - * @type {YXmlElement} - */ - const el = new YXmlElement(this.nodeName) + const el = this._copy() const attrs = this.getAttributes() object.forEach(attrs, (value, key) => { if (typeof value === 'string') { @@ -154,17 +148,17 @@ export class YXmlElement extends YXmlFragment { /** * Sets or updates an attribute. * - * @template {keyof KV & string} KEY + * @template {keyof Attrs & string} KEY * * @param {KEY} attributeName The attribute name that is to be set. - * @param {KV[KEY]} attributeValue The attribute value that is to be set. + * @param {Attrs[KEY]} attributeValue The attribute value that is to be set. * * @public */ setAttribute (attributeName, attributeValue) { if (this.doc !== null) { transact(this.doc, transaction => { - typeMapSet(transaction, this, attributeName, attributeValue) + typeMapSet(transaction, this, attributeName, /** @type {any} */ (attributeValue)) }) } else { /** @type {Map} */ (this._prelimAttrs).set(attributeName, attributeValue) @@ -174,11 +168,11 @@ export class YXmlElement extends YXmlFragment { /** * Returns an attribute value that belongs to the attribute name. * - * @template {keyof KV & string} KEY + * @template {keyof Attrs & string} KEY * * @param {KEY} attributeName The attribute name that identifies the * queried value. - * @return {KV[KEY]|undefined} The queried attribute value. + * @return {Attrs[KEY]|undefined} The queried attribute value. * * @public */ @@ -202,7 +196,7 @@ export class YXmlElement extends YXmlFragment { * Returns all attribute name/value pairs in a JSON Object. * * @param {Snapshot} [snapshot] - * @return {{ [Key in Extract]?: KV[Key]}} A JSON Object that describes the attributes. + * @return {{ [Key in Extract]?: Attrs[Key]}} A JSON Object that describes the attributes. * * @public */ @@ -210,93 +204,6 @@ export class YXmlElement extends YXmlFragment { return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this)) } - /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @param {AbstractAttributionManager} am - * @return {{ nodeName: string, children: delta.ArrayDeltaBuilder>, attributes: import('./AbstractType.js').MapAttributedContent }} - * - * @public - */ - getContentDeep (am = noAttributionsManager) { - const { children: origChildren, attributes: origAttributes } = this.getContent(am) - const children = origChildren.map(d => /** @type {any} */ ( - (d instanceof delta.InsertArrayOp && d.insert instanceof Array) - ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDeltaBuilder>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution) - : d - )) - /** - * @todo there is a Attributes type and a DeepAttributes type. - * @type {delta.MapDeltaBuilder} - */ - const attributes = delta.createMapDelta() - origAttributes.forEach( - null, - (insertOp, key) => { - if (insertOp.value instanceof AbstractType) { - attributes.set(key, insertOp.value.getContentDeep(am), null, insertOp.attribution) - } else { - attributes.set(key, insertOp.value, undefined, insertOp.attribution) - } - } - ) - return delta.createXmlDelta(this.nodeName, children, attributes) - } - - /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @param {import('../internals.js').AbstractAttributionManager} am - * - * @public - */ - getContent (am = noAttributionsManager) { - const { children } = super.getContent(am) - const attributes = typeMapGetDelta(this, am) - return new delta.XmlDelta(this.nodeName, children, attributes) - } - - /** - * Creates a Dom Element that mirrors this YXmlElement. - * - * @param {Document} [_document=document] The document object (you must define - * this when calling this method in - * nodejs) - * @param {Object} [hooks={}] Optional property to customize how hooks - * are presented in the DOM - * @param {any} [binding] You should not set this property. This is - * used if DomBinding wants to create a - * association to the created DOM type. - * @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} - * - * @public - */ - toDOM (_document = document, hooks = {}, binding) { - const dom = _document.createElement(this.nodeName) - const attrs = this.getAttributes() - for (const key in attrs) { - const value = attrs[key] - if (typeof value === 'string') { - dom.setAttribute(key, value) - } - } - typeListForEach(this, yxml => { - dom.appendChild(yxml.toDOM(_document, hooks, binding)) - }) - if (binding !== undefined) { - binding._createAssociation(dom, this) - } - return dom - } - /** * Transform the properties of this type to binary and write it to an * BinaryEncoder. @@ -313,7 +220,7 @@ export class YXmlElement extends YXmlFragment { /** * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder - * @return {YXmlElement} + * @return {import('../utils/types.js').YType} * * @function */ diff --git a/src/types/YXmlEvent.js b/src/types/YXmlEvent.js deleted file mode 100644 index f18a06e89..000000000 --- a/src/types/YXmlEvent.js +++ /dev/null @@ -1,39 +0,0 @@ -import { - YEvent, - YXmlText, YXmlElement, YXmlFragment, Transaction // eslint-disable-line -} from '../internals.js' - -/** - * @extends YEvent - * An Event that describes changes on a YXml Element or Yxml Fragment - */ -export class YXmlEvent extends YEvent { - /** - * @param {YXmlElement|YXmlText|YXmlFragment} target The target on which the event is created. - * @param {Set} subs The set of changed attributes. `null` is included if the - * child list changed. - * @param {Transaction} transaction The transaction instance with which the - * change was created. - */ - constructor (target, subs, transaction) { - super(target, transaction) - /** - * Whether the children changed. - * @type {Boolean} - * @private - */ - this.childListChanged = false - /** - * Set of all changed attributes. - * @type {Set} - */ - this.attributesChanged = new Set() - subs.forEach((sub) => { - if (sub === null) { - this.childListChanged = true - } else { - this.attributesChanged.add(sub) - } - }) - } -} diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 403059b6f..50677afbb 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -3,8 +3,7 @@ */ import { - YXmlEvent, - YXmlElement, + YEvent, AbstractType, typeListMap, typeListForEach, @@ -18,14 +17,11 @@ import { typeListGet, typeListSlice, warnPrematureAccess, - noAttributionsManager, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, // eslint-disable-line - typeListGetContent + YXmlElement, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, YXmlText, YXmlHook // eslint-disable-line } from '../internals.js' -import * as delta from '../utils/Delta.js' +import * as delta from 'lib0/delta' // eslint-disable-line import * as error from 'lib0/error' -import * as array from 'lib0/array' /** * Define the elements to which a set of CSS queries apply. @@ -48,83 +44,6 @@ import * as array from 'lib0/array' * @return {boolean} Whether to include the Dom node in the YXmlElement. */ -/** - * Represents a subset of the nodes of a YXmlElement / YXmlFragment and a - * position within them. - * - * Can be created with {@link YXmlFragment#createTreeWalker} - * - * @public - * @implements {Iterable} - */ -export class YXmlTreeWalker { - /** - * @param {YXmlFragment | YXmlElement} root - * @param {function(AbstractType):boolean} [f] - */ - constructor (root, f = () => true) { - this._filter = f - this._root = root - /** - * @type {Item} - */ - this._currentNode = /** @type {Item} */ (root._start) - this._firstCall = true - root.doc ?? warnPrematureAccess() - } - - [Symbol.iterator] () { - return this - } - - /** - * Get the next node. - * - * @return {IteratorResult} The next node. - * - * @public - */ - next () { - /** - * @type {Item|null} - */ - let n = this._currentNode - let type = n && n.content && /** @type {any} */ (n.content).type - if (n !== null && (!this._firstCall || n.deleted || !this._filter(type))) { // if first call, we check if we can use the first item - do { - type = /** @type {any} */ (n.content).type - if (!n.deleted && (type.constructor === YXmlElement || type.constructor === YXmlFragment) && type._start !== null) { - // walk down in the tree - n = type._start - } else { - // walk right or up in the tree - while (n !== null) { - /** - * @type {Item | null} - */ - const nxt = n.next - if (nxt !== null) { - n = nxt - break - } else if (n.parent === this._root) { - n = null - } else { - n = /** @type {AbstractType} */ (n.parent)._item - } - } - } - } while (n !== null && (n.deleted || !this._filter(/** @type {ContentType} */ (n.content).type))) - } - this._firstCall = false - if (n === null) { - // @ts-ignore - return { value: undefined, done: true } - } - this._currentNode = n - return { value: /** @type {any} */ (n.content).type, done: false } - } -} - /** * Represents a list of {@link YXmlElement}.and {@link YXmlText} types. * A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a @@ -132,7 +51,9 @@ export class YXmlTreeWalker { * element - in this case the attributes and the nodeName are not shared. * * @public - * @extends AbstractType + * @template {any} [Children=any] + * @template {{[K in string]:any}} [Attrs={}] + * @extends AbstractType> */ export class YXmlFragment extends AbstractType { constructor () { @@ -159,7 +80,7 @@ export class YXmlFragment extends AbstractType { * * Observer functions are fired * * @param {Doc} y The Yjs instance - * @param {Item} item + * @param {Item?} item */ _integrate (y, item) { super._integrate(y, item) @@ -167,20 +88,15 @@ export class YXmlFragment extends AbstractType { this._prelimContent = null } - _copy () { - return new YXmlFragment() - } - /** * Makes a copy of this data type that can be included somewhere else. * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {YXmlFragment} + * @return {this} */ clone () { - const el = new YXmlFragment() - // @ts-ignore + const el = this._copy() el.insert(0, this.toArray().map(item => item instanceof AbstractType ? item.clone() : item)) return el } @@ -190,71 +106,6 @@ export class YXmlFragment extends AbstractType { return this._prelimContent === null ? this._length : this._prelimContent.length } - /** - * Create a subtree of childNodes. - * - * @example - * const walker = elem.createTreeWalker(dom => dom.nodeName === 'div') - * for (let node in walker) { - * // `node` is a div node - * nop(node) - * } - * - * @param {function(AbstractType):boolean} filter Function that is called on each child element and - * returns a Boolean indicating whether the child - * is to be included in the subtree. - * @return {YXmlTreeWalker} A subtree and a position within it. - * - * @public - */ - createTreeWalker (filter) { - return new YXmlTreeWalker(this, filter) - } - - /** - * Returns the first YXmlElement that matches the query. - * Similar to DOM's {@link querySelector}. - * - * Query support: - * - tagname - * TODO: - * - id - * - attribute - * - * @param {CSS_Selector} query The query on the children. - * @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null. - * - * @public - */ - querySelector (query) { - query = query.toUpperCase() - // @ts-ignore - const iterator = new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query) - const next = iterator.next() - if (next.done) { - return null - } else { - return next.value - } - } - - /** - * Returns all YXmlElements that match the query. - * Similar to Dom's {@link querySelectorAll}. - * - * @todo Does not yet support all queries. Currently only query by tagName. - * - * @param {CSS_Selector} query The query on the children - * @return {Array} The elements that match this query. - * - * @public - */ - querySelectorAll (query) { - query = query.toUpperCase() - // @ts-ignore - return array.from(new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query)) - } - /** * Creates YXmlEvent and calls observers. * @@ -262,7 +113,7 @@ export class YXmlFragment extends AbstractType { * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. */ _callObserver (transaction, parentSubs) { - callTypeObservers(this, transaction, new YXmlEvent(this, parentSubs, transaction)) + callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs)) } /** @@ -281,32 +132,6 @@ export class YXmlFragment extends AbstractType { return this.toString() } - /** - * Creates a Dom Element that mirrors this YXmlElement. - * - * @param {Document} [_document=document] The document object (you must define - * this when calling this method in - * nodejs) - * @param {Object} [hooks={}] Optional property to customize how hooks - * are presented in the DOM - * @param {any} [binding] You should not set this property. This is - * used if DomBinding wants to create a - * association to the created DOM type. - * @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} - * - * @public - */ - toDOM (_document = document, hooks = {}, binding) { - const fragment = _document.createDocumentFragment() - if (binding !== undefined) { - binding._createAssociation(fragment, this) - } - typeListForEach(this, xmlType => { - fragment.insertBefore(xmlType.toDOM(_document, hooks, binding), null) - }) - return fragment - } - /** * Inserts new content at an index. * @@ -315,7 +140,7 @@ export class YXmlFragment extends AbstractType { * xml.insert(0, [new Y.XmlText('text')]) * * @param {number} index The index to insert content at - * @param {Array} content The array of content + * @param {Array} content The array of content */ insert (index, content) { if (this.doc !== null) { @@ -380,36 +205,6 @@ export class YXmlFragment extends AbstractType { return typeListToArray(this) } - /** - * Calculate the attributed content using the attribution manager. - * - * @param {import('../internals.js').AbstractAttributionManager} am - * @return {{ children: import('../utils/Delta.js').ArrayDeltaBuilderBuilder> }} - */ - getContent (am = noAttributionsManager) { - const children = typeListGetContent(this, am) - return { children } - } - - /** - * Calculate the attributed content using the attribution manager. - * - * @param {import('../internals.js').AbstractAttributionManager} am - * @return {{ children: import('../utils/Delta.js').ArrayDeltaBuilderBuilder> }} - */ - getContentDeep (am) { - const { children: origChildren } = this.getContent(am) - /** - * @type {import('../utils/Delta.js').ArrayDeltaBuilderBuilder>} - */ - const children = origChildren.map(d => /** @type {any} */ ( - d instanceof delta.InsertArrayOp && d.insert instanceof Array - ? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution) - : d - )) - return { children } - } - /** * Appends content to this YArray. * @@ -474,7 +269,7 @@ export class YXmlFragment extends AbstractType { /** * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder - * @return {YXmlFragment} + * @return {import('../utils/types.js').YType} * * @private * @function diff --git a/src/types/YXmlHook.js b/src/types/YXmlHook.js index 1bf248465..13e49be25 100644 --- a/src/types/YXmlHook.js +++ b/src/types/YXmlHook.js @@ -22,10 +22,10 @@ export class YXmlHook extends YMap { } /** - * Creates an Item with the same effect as this Item (without position effect) + * @return {this} */ _copy () { - return new YXmlHook(this.hookName) + return /** @type {this} */ (new YXmlHook(this.hookName)) } /** @@ -33,46 +33,16 @@ export class YXmlHook extends YMap { * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {YXmlHook} + * @return {this} */ clone () { - const el = new YXmlHook(this.hookName) + const el = this._copy() this.forEach((value, key) => { el.set(key, value) }) return el } - /** - * Creates a Dom Element that mirrors this YXmlElement. - * - * @param {Document} [_document=document] The document object (you must define - * this when calling this method in - * nodejs) - * @param {Object.} [hooks] Optional property to customize how hooks - * are presented in the DOM - * @param {any} [binding] You should not set this property. This is - * used if DomBinding wants to create a - * association to the created DOM type - * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} - * - * @public - */ - toDOM (_document = document, hooks = {}, binding) { - const hook = hooks[this.hookName] - let dom - if (hook !== undefined) { - dom = hook.createDom(this) - } else { - dom = document.createElement(this.hookName) - } - dom.setAttribute('data-yjs-hook', this.hookName) - if (binding !== undefined) { - binding._createAssociation(dom, this) - } - return dom - } - /** * Transform the properties of this type to binary and write it to an * BinaryEncoder. @@ -89,7 +59,7 @@ export class YXmlHook extends YMap { /** * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder - * @return {YXmlHook} + * @return {import('../utils/types.js').YType} * * @private * @function diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index 7b5ac37c3..8f66ad11c 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -4,11 +4,12 @@ import { ContentType, YXmlElement, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, // eslint-disable-line } from '../internals.js' -import * as delta from '../utils/Delta.js' - /** + * @todo can we deprecate this? + * * Represents text in a Dom Element. In the future this type will also handle * simple formatting information like bold and italic. + * @extends YText */ export class YXmlText extends YText { /** @@ -27,82 +28,19 @@ export class YXmlText extends YText { return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null } - _copy () { - return new YXmlText() - } - /** * Makes a copy of this data type that can be included somewhere else. * * Note that the content is only readable _after_ it has been included somewhere in the Ydoc. * - * @return {YXmlText} + * @return {this} */ clone () { - const text = new YXmlText() + const text = /** @type {this} */ (this._copy()) text.applyDelta(this.getContent()) return text } - /** - * Creates a Dom Element that mirrors this YXmlText. - * - * @param {Document} [_document=document] The document object (you must define - * this when calling this method in - * nodejs) - * @param {Object} [hooks] Optional property to customize how hooks - * are presented in the DOM - * @param {any} [binding] You should not set this property. This is - * used if DomBinding wants to create a - * association to the created DOM type. - * @return {Text} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} - * - * @public - */ - toDOM (_document = document, hooks, binding) { - const dom = _document.createTextNode(this.toString()) - if (binding !== undefined) { - binding._createAssociation(dom, this) - } - return dom - } - - toString () { - return this.getContent().ops.map(dop => { - if (dop instanceof delta.InsertStringOp) { - const nestedNodes = [] - for (const nodeName in dop.attributes) { - const attrs = [] - for (const key in dop.attributes[nodeName]) { - attrs.push({ key, value: dop.attributes[nodeName][key] }) - } - // sort attributes to get a unique order - attrs.sort((a, b) => a.key < b.key ? -1 : 1) - nestedNodes.push({ nodeName, attrs }) - } - // sort node order to get a unique order - nestedNodes.sort((a, b) => a.nodeName < b.nodeName ? -1 : 1) - // now convert to dom string - let str = '' - for (let i = 0; i < nestedNodes.length; i++) { - const node = nestedNodes[i] - str += `<${node.nodeName}` - for (let j = 0; j < node.attrs.length; j++) { - const attr = node.attrs[j] - str += ` ${attr.key}="${attr.value}"` - } - str += '>' - } - str += dop.insert - for (let i = nestedNodes.length - 1; i >= 0; i--) { - str += `` - } - return str - } - return '' - }).join('') - } - /** * @return {string} */ @@ -119,10 +57,10 @@ export class YXmlText extends YText { } /** - * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder - * @return {YXmlText} + * @param {UpdateDecoderV1 | UpdateDecoderV2} _decoder + * @return {import('../utils/types.js').YType} * * @private * @function */ -export const readYXmlText = decoder => new YXmlText() +export const readYXmlText = _decoder => new YXmlText() diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 0dc20073e..745bc8415 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -33,13 +33,13 @@ import { ObservableV2 } from 'lib0/observable' import * as encoding from 'lib0/encoding' import * as s from 'lib0/schema' -export const attributionJsonSchema = s.object({ - insert: s.array(s.string).optional, - insertedAt: s.number.optional, - delete: s.array(s.string).optional, - deletedAt: s.number.optional, - attributes: s.record(s.string, s.array(s.string)).optional, - attributedAt: s.number.optional +export const attributionJsonSchema = s.$object({ + insert: s.$array(s.$string).optional, + insertedAt: s.$number.optional, + delete: s.$array(s.$string).optional, + deletedAt: s.$number.optional, + attributes: s.$record(s.$string, s.$array(s.$string)).optional, + attributedAt: s.$number.optional }) /** @@ -63,7 +63,7 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { */ const attribution = {} if (deleted) { - attribution.delete = s.array(s.string).ensure([]) + attribution.delete = s.$array(s.$string).cast([]) } else { attribution.insert = [] } @@ -72,7 +72,7 @@ export const createAttributionFromAttributionItems = (attrs, deleted) => { // eslint-disable-next-line no-fallthrough case 'insert': case 'delete': { - const as = /** @type {import('../utils/Delta.js').Attribution_} */ (attribution) + const as = /** @type {import('lib0/delta').Attribution} */ (attribution) const ls = as[attr.name] = as[attr.name] ?? [] ls.push(attr.val) break diff --git a/src/utils/Delta.js b/src/utils/Delta.js deleted file mode 100644 index 4f3672461..000000000 --- a/src/utils/Delta.js +++ /dev/null @@ -1,1007 +0,0 @@ -import * as object from 'lib0/object' -import * as map from 'lib0/map' -import * as fun from 'lib0/function' -import * as traits from 'lib0/traits' -import * as error from 'lib0/error' -import * as s from 'lib0/schema' -import { attributionJsonSchema } from './AttributionManager.js' - -/** - * @template {any} ArrayContent - * @template {object} Embeds - * @template {Delta|undefined} ModifyingDelta - * @typedef {InsertStringOp|InsertEmbedOp|InsertArrayOp|RetainOp|DeleteOp|(ModifyingDelta extends undefined ? never : ModifyOp)} DeltaOp - */ - -/** - * @template {object} Embeds - * @template {Delta|undefined} Modifiers - * @typedef {InsertStringOp|InsertEmbedOp|RetainOp|DeleteOp|(Modifiers extends undefined ? never : ModifyOp)} TextDeltaOp - */ - -/** - * @template ArrayContent - * @typedef {InsertArrayOp|RetainOp|DeleteOp} ArrayDeltaOp - */ - -/** - * @typedef {import('./AttributionManager.js').Attribution} Attribution_ - */ - -/** - * @typedef {{ [key: string]: any }} FormattingAttributes - */ - -/** - * @typedef {Array} DeltaJson - */ - -/** - * @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution_ } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution_ } | { modify: object }} DeltaJsonOp - */ - -export class InsertStringOp { - /** - * @param {string} insert - * @param {FormattingAttributes|null} attributes - * @param {Attribution_|null} attribution - */ - constructor (insert, attributes, attribution) { - this.insert = insert - this.attributes = attributes - this.attribution = attribution - } - - /** - * @return {'insert'} - */ - get type () { - return 'insert' - } - - get length () { - return (this.insert.constructor === Array || this.insert.constructor === String) ? this.insert.length : 1 - } - - /** - * @return {DeltaJsonOp} - */ - toJSON () { - return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) - } - - /** - * @param {InsertStringOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) - } -} - -/** - * @template {any} ArrayContent - */ -export class InsertArrayOp { - /** - * @param {Array} insert - * @param {FormattingAttributes|null} attributes - * @param {Attribution_|null} attribution - */ - constructor (insert, attributes, attribution) { - this.insert = insert - this.attributes = attributes - this.attribution = attribution - } - - /** - * @return {'insert'} - */ - get type () { - return 'insert' - } - - get length () { - return this.insert.length - } - - /** - * @return {DeltaJsonOp} - */ - toJSON () { - return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) - } - - /** - * @param {InsertArrayOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) - } -} - -/** - * @template {object} Embeds - */ -export class InsertEmbedOp { - /** - * @param {Embeds} insert - * @param {FormattingAttributes|null} attributes - * @param {Attribution_|null} attribution - */ - constructor (insert, attributes, attribution) { - this.insert = insert - this.attributes = attributes - this.attribution = attribution - } - - /** - * @return {'insertEmbed'} - */ - get type () { - return 'insertEmbed' - } - - get length () { - return 1 - } - - /** - * @return {DeltaJsonOp} - */ - toJSON () { - return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({})) - } - - /** - * @param {InsertEmbedOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) - } -} - -export class DeleteOp { - /** - * @param {number} len - */ - constructor (len) { - this.delete = len - } - - /** - * @return {'delete'} - */ - get type () { - return 'delete' - } - - get length () { - return 0 - } - - /** - * @return {DeltaJsonOp} - */ - toJSON () { - return { delete: this.delete } - } - - /** - * @param {DeleteOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return this.delete === other.delete - } -} - -export 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 - } - - /** - * @return {'retain'} - */ - get type () { - return 'retain' - } - - get length () { - return this.retain - } - - /** - * @return {DeltaJsonOp} - */ - toJSON () { - return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {}) - } - - /** - * @param {RetainOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return this.retain === other.retain && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution) - } -} - -/** - * Delta that can be applied on a YType Embed - * - * @template {Delta} DTypes - */ -export class ModifyOp { - /** - * @param {DTypes} delta - */ - constructor (delta) { - this.modify = delta - } - - /** - * @return {'modify'} - */ - get type () { - return 'modify' - } - - get length () { - return 1 - } - - /** - * @return {DeltaJsonOp} - */ - toJSON () { - return { modify: this.modify.toJSON() } - } - - /** - * @param {ModifyOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return this.modify[traits.EqualityTraitSymbol](other.modify) - } -} - -export class AbstractDelta { - constructor () { - this.remote = false - /** - * @type {any} origin - */ - this.origin = null - this.isDiff = true - } - - /** - * @param {any} _other - */ - [traits.EqualityTraitSymbol] (_other) { - error.methodUnimplemented() - } -} - -/** - * @template {Delta|undefined} [Modifiers=any] - * @typedef {(TextDelta | ArrayDelta | MapDelta | XmlDelta )} Delta - */ - -/** - * @template {'array' | 'text' | 'custom'} Type - * @template {DeltaOp} TDeltaOp - * @template {Delta|undefined} Modifiers - */ -export class AbstractArrayDelta extends AbstractDelta { - /** - * @param {Type} type - */ - constructor (type) { - super() - this.type = type - /** - * @type {Array} - */ - this.ops = [] - } - - /** - * @template {(d:TDeltaOp) => DeltaOp} Mapper - * @param {Mapper} f - * @return {AbstractArrayDeltaBuilder infer OP ? OP : unknown,Modifiers>} - */ - map (f) { - const d = /** @type {AbstractArrayDeltaBuilder} */ (new /** @type {any} */ (this.constructor)(this.type)) - d.ops = this.ops.map(f) - // @ts-ignore - d.lastOp = d.ops[d.ops.length - 1] ?? null - return d - } - - /** - * - * Iterate through the changes. There are two approches to iterate through the changes. The - * following examples achieve the same thing: - * - * @example - * d.forEach((op, index) => { - * if (op instanceof delta.InsertArrayOp) { - * op.insert - * } else if (op instanceof delta.RetainOp ) { - * op.retain - * } else if (op instanceof delta.DeleteOp) { - * op.delete - * } - * }) - * - * The second approach doesn't require instanceof checks. - * - * @example - * d.forEach(null, - * (insertOp, index) => insertOp.insert, - * (retainOp, index) => insertOp.retain - * (deleteOp, index) => insertOp.delete - * ) - * - * @param {null|((d:TDeltaOp,index:number)=>void)} f - * @param {null|((insertOp:Exclude>,index:number)=>void)} insertHandler - * @param {null|((retainOp:RetainOp,index:number)=>void)} retainHandler - * @param {null|((deleteOp:DeleteOp,index:number)=>void)} deleteHandler - * @param {null|(Modifiers extends undefined ? null : ((modifyOp:ModifyOp,index:number)=>void))} modifyHandler - */ - forEach (f = null, insertHandler = null, retainHandler = null, deleteHandler = null, modifyHandler = null) { - for ( - let i = 0, index = 0, op = this.ops[i]; - i < this.ops.length; - i++, index += op.length, op = this.ops[i] - ) { - f?.(op, index) - switch (op.constructor) { - case RetainOp: - retainHandler?.(/** @type {RetainOp} */ (op), index) - break - case DeleteOp: - deleteHandler?.(/** @type {DeleteOp} */ (op), index) - break - case ModifyOp: - modifyHandler?.(/** @type {any}) */ (op), index) - break - default: - insertHandler?.(/** @type {any} */ (op), index) - } - } - } - - /** - * @param {AbstractArrayDelta} other - * @return {boolean} - */ - equals (other) { - return this[traits.EqualityTraitSymbol](other) - } - - /** - * @returns {DeltaJson} - */ - toJSON () { - return this.ops.map(o => o.toJSON()) - } - - /** - * @param {AbstractArrayDelta} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.ops, other.ops) - } -} - -/** - * @template {object} Vals - * @template {keyof Vals} K - * @template {Delta|undefined} Modifiers - * @typedef {(change:MapDeltaChange,key:K) => void} MapDeltaChangeCallback - */ - -/** - * @template V - */ -class MapInsertOp { - /** - * @param {V} value - * @param {V|undefined} prevValue - * @param {Attribution_?} attribution - */ - constructor (value, prevValue, attribution) { - this.prevValue = prevValue - this.attribution = attribution - this.value = value - } - - /** - * @return {'insert'} - */ - get type () { return 'insert' } - - toJSON () { - return { - type: this.type, - value: this.value, - prevValue: this.prevValue, - attribution: this.attribution - } - } - - /** - * @param {MapInsertOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.value, other.value) && fun.equalityDeep(this.prevValue, other.prevValue) && fun.equalityDeep(this.attribution, other.attribution) - } -} - -/** - * @template V - */ -class MapDeleteOp { - /** - * @param {V|undefined} prevValue - * @param {Attribution_?} attribution - */ - constructor (prevValue, attribution) { - this.prevValue = prevValue - this.attribution = attribution - } - - get value () { return undefined } - - /** - * @type {'delete'} - */ - get type () { return 'delete' } - - toJSON () { - return { - type: this.type, - prevValue: this.prevValue, - attribution: this.attribution - } - } - - /** - * @param {MapDeleteOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.prevValue, other.prevValue) && fun.equalityDeep(this.attribution, other.attribution) - } -} - -/** - * @template {Delta} Modifiers - */ -class MapModifyOp { - /** - * @param {Modifiers} delta - */ - constructor (delta) { - this.modify = delta - } - - get value () { return undefined } - - /** - * @type {'modify'} - */ - get type () { return 'modify' } - - toJSON () { - return { - type: this.type, - modify: this.modify.toJSON() - } - } - - /** - * @param {MapModifyOp} other - */ - [traits.EqualityTraitSymbol] (other) { - return this.modify[traits.EqualityTraitSymbol](other.modify) - } -} - -/** - * @template V - * @template {Delta|undefined} Modifiers - * @typedef {MapInsertOp | MapDeleteOp | (Modifiers extends undefined ? never : MapModifyOp)} MapDeltaChange - */ - -export const mapDeltaChangeJsonSchema = s.union( - s.object({ type: s.literal('insert'), value: s.any, prevValue: s.any.optional, attribution: attributionJsonSchema.nullable.optional }), - s.object({ type: s.literal('delete'), prevValue: s.any.optional, attribution: attributionJsonSchema.nullable.optional }), - s.object({ type: s.literal('modify'), modify: s.any }) -) - -export const mapDeltaJsonSchema = s.record(s.string, mapDeltaChangeJsonSchema) - -/** - * @template {object} Vals - * @template {Delta|undefined} Modifiers - */ -export class MapDelta extends AbstractDelta { - constructor () { - super() - /** - * @type {Map>} - */ - this.changes = map.create() - /** - * @type {Attribution_?} - */ - this.usedAttribution = null - } - - /** - * - * Iterate through the changes. There are two approches to iterate through changes. The - * following two examples achieve the same thing: - * - * @example - * d.forEach((op, index) => { - * if (op instanceof delta.InsertArrayOp) { - * op.insert - * } else if (op instanceof delta.RetainOp ) { - * op.retain - * } else if (op instanceof delta.DeleteOp) { - * op.delete - * } else if (op instanceof delta.ModifyOp) { - * op.modify - * } - * }) - * - * The second approach doesn't require instanceof checks. - * - * @example - * d.forEach(null, - * (insertOp, index) => insertOp.insert, - * (retainOp, index) => insertOp.retain - * (deleteOp, index) => insertOp.delete - * (modifyOp, index) => insertOp.modify - * ) - * - * @param {null|((change:MapDeltaChange,key:keyof Vals)=>void)} changeHandler - * @param {null|((insertOp:MapInsertOp,key:keyof Vals)=>void)} insertHandler - * @param {null|((deleteOp:MapDeleteOp,key:keyof Vals)=>void)} deleteHandler - * @param {null|((modifyOp:(MapModifyOp),key:keyof Vals)=>void)} modifyHandler - */ - forEach (changeHandler = null, insertHandler = null, deleteHandler = null, modifyHandler = null) { - this.changes.forEach((change, key) => { - changeHandler?.(change, key) - switch (change.constructor) { - case MapDeleteOp: - deleteHandler?.(/** @type {MapDeleteOp} */ (change), key) - break - case MapInsertOp: - insertHandler?.(/** @type {MapInsertOp} */ (change), key) - break - case MapModifyOp: - modifyHandler?.(/** @type {MapModifyOp} */ (change), key) - break - } - }) - } - - /** - * @template {keyof Vals} K - * - * @param {K} key - * @return {MapDeltaChange | undefined} - */ - get (key) { - return /** @type {MapDeltaChange | undefined} */ (this.changes.get(key)) - } - - /** - * @param {keyof Vals} key - */ - has (key) { - return this.changes.has(key) - } - - /** - * @param {MapDelta} other - * @return {boolean} - */ - equals (other) { - return this[traits.EqualityTraitSymbol](other) - } - - /** - * @return {s.Unwrap} - */ - toJSON () { - /** - * @type {s.Unwrap} - */ - const changes = {} - this.changes.forEach((change, key) => { - changes[/** @type {string} */ (key)] = change.toJSON() - }) - return changes - } - - /** - * Preferred way to iterate through changes. - * - * @return {IterableIterator<{ [K in keyof Vals]: [K, MapDeltaChange] }[keyof Vals]>} - */ - [Symbol.iterator] () { - // @ts-ignore - return this.changes.entries() - } - - /** - * @param {MapDelta} other - */ - [traits.EqualityTraitSymbol] (other) { - return fun.equalityDeep(this.changes, other.changes) - } - - /** - * @return {MapDelta} - */ - done () { - return this - } -} - -/** - * @template {string|undefined} NodeName - * @template Children - * @template {object} Attrs - * @template {Delta|undefined} [ChildModifiers=undefined] - * @template {Delta|undefined} [AttrModifiers=undefined] - * @template {'done'|'mutable'} [Done='mutable'] - */ -export class XmlDelta extends AbstractDelta { - /** - * @param {NodeName} nodeName - * @param {ArrayDeltaBuilder} children - * @param {MapDelta} attributes - */ - constructor (nodeName, children, attributes) { - super() - this.nodeName = nodeName - /** - * @type {ArrayDeltaBuilder} - */ - this.children = children - /** - * @type {Done extends 'mutable' ? MapDeltaBuilder : MapDelta} - */ - this.attributes = /** @type {any} */ (attributes) - } - - toJSON () { - return { - nodeName: this.nodeName, - children: this.children.toJSON(), - attributes: this.attributes.toJSON() - } - } - - /** - * @return {XmlDelta} - */ - done () { - this.children.done() - this.attributes.done() - return /** @type {any} */ (this) - } - - /** - * @param {XmlDelta} other - */ - [traits.EqualityTraitSymbol] (other) { - return this.nodeName === other.nodeName && this.children[traits.EqualityTraitSymbol](other.children) && this.attributes[traits.EqualityTraitSymbol](other.attributes) - } -} - -/** - * @template {string|undefined} NodeName - * @template Children - * @template {object} Attrs - * @template {Delta|undefined} [ChildModifiers=undefined] - * @template {Delta|undefined} [AttrModifiers=undefined] - * @param {NodeName} nodeName - * @param {ArrayDeltaBuilder} children - * @param {MapDeltaBuilder} attributes - * @return {XmlDelta} - */ -export const createXmlDelta = (nodeName, children = createArrayDelta(), attributes = /** @type {any} */ (createMapDelta())) => new XmlDelta(nodeName, children, attributes) - -/** - * @template {object} Vals - * @template {Delta|undefined} [Modifiers=undefined] - * @extends MapDelta - */ -export class MapDeltaBuilder extends MapDelta { - /** - * @template {keyof Vals} K - * @param {K} key - * @param {Modifiers} delta - */ - modify (key, delta) { - this.changes.set(key, /** @type {any} */ ({ type: 'modify', delta })) - return this - } - - /** - * @template {keyof Vals} K - * @param {K} key - * @param {Vals[K]} newVal - * @param {Vals[K]|undefined} prevValue - * @param {Attribution_?} attribution - */ - set (key, newVal, prevValue = undefined, attribution = null) { - const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) - this.changes.set(key, new MapInsertOp(newVal, prevValue, mergedAttribution)) - return this - } - - /** - * @template {keyof Vals} K - * @param {K} key - * @param {Vals[K]|undefined} prevValue - * @param {Attribution_?} attribution - */ - delete (key, prevValue = undefined, attribution = null) { - const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) - this.changes.set(key, new MapDeleteOp(prevValue, mergedAttribution)) - return this - } - - /** - * @param {Attribution_?} attribution - */ - useAttribution (attribution) { - this.usedAttribution = attribution - return this - } -} - -export const createMapDelta = () => new MapDeltaBuilder() - -/** - * 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) => object.isEmpty(a) ? b : (object.isEmpty(b) ? a : object.assign({}, a, b)) - -/** - * @template {'array' | 'text' | 'custom'} Type - * @template {DeltaOp} TDeltaOp - * @template {Delta|undefined} Modifiers - * @extends AbstractArrayDelta - */ -export class AbstractArrayDeltaBuilder extends AbstractArrayDelta { - /** - * @param {Type} type - */ - constructor (type) { - super(type) - /** - * @type {FormattingAttributes?} - */ - this.usedAttributes = null - /** - * @type {Attribution_?} - */ - this.usedAttribution = null - /** - * @type {TDeltaOp?} - */ - this.lastOp = null - } - - /** - * @param {FormattingAttributes?} attributes - * @return {this} - */ - useAttributes (attributes) { - this.usedAttributes = attributes - return this - } - - /** - * @param {string} name - * @param {any} value - */ - updateUsedAttributes (name, value) { - if (value == null) { - this.usedAttributes = object.assign({}, this.usedAttributes) - delete this.usedAttributes?.[name] - if (object.isEmpty(this.usedAttributes)) { - this.usedAttributes = null - } - } else if (!fun.equalityDeep(this.usedAttributes?.[name], value)) { - this.usedAttributes = object.assign({}, this.usedAttributes) - this.usedAttributes[name] = value - } - return this - } - - /** - * @template {keyof Attribution_} NAME - * @param {NAME} name - * @param {Attribution_[NAME]?} value - */ - updateUsedAttribution (name, value) { - if (value == null) { - this.usedAttribution = object.assign({}, this.usedAttribution) - delete this.usedAttribution?.[name] - if (object.isEmpty(this.usedAttribution)) { - this.usedAttribution = null - } - } else if (!fun.equalityDeep(this.usedAttribution?.[name], value)) { - this.usedAttribution = object.assign({}, this.usedAttribution) - this.usedAttribution[name] = value - } - return this - } - - /** - * @param {Attribution_?} attribution - */ - useAttribution (attribution) { - this.usedAttribution = attribution - return this - } - - /** - * @param {(TDeltaOp extends InsertStringOp ? string : never) | (TDeltaOp extends InsertEmbedOp ? (Embeds) : never) | (TDeltaOp extends InsertArrayOp ? Array : never) } insert - * @param {FormattingAttributes?} attributes - * @param {Attribution_?} attribution - * @return {this} - */ - insert (insert, attributes = null, attribution = null) { - const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) - const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) - if (((this.lastOp instanceof InsertStringOp && insert.constructor === String) || (this.lastOp instanceof InsertArrayOp && insert.constructor === Array)) && (mergedAttributes === this.lastOp.attributes || fun.equalityDeep(mergedAttributes, this.lastOp.attributes)) && (mergedAttribution === this.lastOp.attribution || fun.equalityDeep(mergedAttribution, this.lastOp.attribution))) { - // @ts-ignore - if (insert.constructor === String) { - // @ts-ignore - this.lastOp.insert += insert - } else { - // @ts-ignore - this.lastOp.insert.push(...insert) - } - } else { - const OpConstructor = /** @type {any} */ (insert.constructor === String ? InsertStringOp : (insert.constructor === Array ? InsertArrayOp : InsertEmbedOp)) - this.ops.push(this.lastOp = new OpConstructor(insert, object.isEmpty(mergedAttributes) ? null : mergedAttributes, object.isEmpty(mergedAttribution) ? null : mergedAttribution)) - } - return this - } - - /** - * @param {number} retain - * @param {FormattingAttributes?} attributes - * @param {Attribution_?} attribution - * @return {this} - */ - retain (retain, attributes = null, attribution = null) { - const mergedAttributes = mergeAttrs(this.usedAttributes, attributes) - const mergedAttribution = mergeAttrs(this.usedAttribution, attribution) - if (this.lastOp instanceof RetainOp && fun.equalityDeep(mergedAttributes, this.lastOp.attributes) && fun.equalityDeep(mergedAttribution, this.lastOp.attribution)) { - this.lastOp.retain += retain - } else { - // @ts-ignore - this.ops.push(this.lastOp = new RetainOp(retain, mergedAttributes, mergedAttribution)) - } - return this - } - - /** - * @param {number} len - * @return {this} - */ - delete (len) { - if (this.lastOp instanceof DeleteOp) { - this.lastOp.delete += len - } else { - // @ts-ignore - this.ops.push(this.lastOp = new DeleteOp(len)) - } - return this - } - - /** - * @return {Type extends 'array' ? ArrayDelta : (Type extends 'text' ? TextDelta : AbstractArrayDelta)} - */ - done () { - while (this.lastOp != null && this.lastOp instanceof RetainOp && this.lastOp.attributes === null && this.lastOp.attribution === null) { - this.ops.pop() - this.lastOp = this.ops[this.ops.length - 1] ?? null - } - return /** @type {any} */ (this) - } -} - -/** - * @template {any} ArrayContent - * @template {Delta|undefined} Modifiers - * @extends AbstractArrayDeltaBuilder<'array', ArrayDeltaOp,Modifiers> - */ -export class ArrayDeltaBuilder extends AbstractArrayDeltaBuilder { - constructor () { - super('array') - } -} - -/** - * @template {any} ArrayContent - * @template {Delta|undefined} Modifiers - * @typedef {AbstractArrayDelta<'array', ArrayDeltaOp,Modifiers>} ArrayDelta - */ - -/** - * @template {object} Embeds - * @template {Delta|undefined} Modifiers - * @typedef {AbstractArrayDelta<'text',TextDeltaOp,Modifiers>} TextDelta - */ - -/** - * @template {object} Embeds - * @template {Delta|undefined} [Modifiers=undefined] - * @extends AbstractArrayDeltaBuilder<'text',TextDeltaOp,Modifiers> - */ -export class TextDeltaBuilder extends AbstractArrayDeltaBuilder { - constructor () { - super('text') - } -} - -/** - * @template {object} [Embeds=any] - * @template {Delta|undefined} [Modifiers=undefined] - * @return {TextDeltaBuilder} - */ -export const createTextDelta = () => new TextDeltaBuilder() - -/** - * @template [V=any] - * @template {Delta|undefined} [Modifiers=undefined] - * @return {ArrayDeltaBuilder} - */ -export const createArrayDelta = () => new ArrayDeltaBuilder() - -/** - * @template {'custom' | 'text' | 'array'} T - * @param {DeltaJson} ops - * @param {T} type - */ -export const fromJSON = (ops, type) => { - const d = new AbstractArrayDeltaBuilder(type) - for (let i = 0; i < ops.length; i++) { - const op = /** @type {any} */ (ops[i]) - // @ts-ignore - if (op.insert !== undefined) { - d.insert(op.insert, op.attributes, op.attribution) - } else if (op.retain !== undefined) { - d.retain(op.retain, op.attributes ?? null, op.attribution ?? null) - } else if (op.delete !== undefined) { - d.delete(op.delete) - } else { - error.unexpectedCase() - } - } - return d.done() -} diff --git a/src/utils/Doc.js b/src/utils/Doc.js index c116f3f9f..58590d34b 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -12,7 +12,7 @@ import { YXmlFragment, transact, applyUpdate, - ContentDoc, Item, Transaction, YEvent, // eslint-disable-line + ContentDoc, Item, Transaction, // eslint-disable-line encodeStateAsUpdate } from '../internals.js' @@ -24,6 +24,13 @@ import * as promise from 'lib0/promise' export const generateNewClientId = random.uint32 +/** + * @typedef {import('../utils/types.js').YTypeConstructors} YTypeConstructors + */ +/** + * @typedef {import('../utils/types.js').YType} YType + */ + /** * @typedef {Object} DocOpts * @property {boolean} [DocOpts.gc=true] Disable garbage collection (default: gc=true) @@ -72,7 +79,7 @@ export class Doc extends ObservableV2 { this.isSuggestionDoc = isSuggestionDoc this.cleanupFormatting = !isSuggestionDoc /** - * @type {Map>>} + * @type {Map} */ this.share = new Map() this.store = new StructStore() @@ -205,7 +212,7 @@ export class Doc extends ObservableV2 { * Define all types right after the Y.Doc instance is created and store them in a separate object. * Also use the typed methods `getText(name)`, `getArray(name)`, .. * - * @template {typeof AbstractType} Type + * @template {YTypeConstructors} TypeC * @example * const ydoc = new Y.Doc(..) * const appState = { @@ -214,8 +221,8 @@ export class Doc extends ObservableV2 { * } * * @param {string} name - * @param {Type} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ... - * @return {InstanceType} The created type. Constructed with TypeConstructor + * @param {TypeC} TypeConstructor The constructor of the type definition. E.g. Y.Text, Y.Array, Y.Map, ... + * @return {InstanceType} The created type. Constructed with TypeConstructor * * @public */ @@ -227,6 +234,7 @@ export class Doc extends ObservableV2 { return t }) const Constr = type.constructor + // @ts-ignore if (TypeConstructor !== AbstractType && Constr !== TypeConstructor) { if (Constr === AbstractType) { // @ts-ignore @@ -245,12 +253,12 @@ export class Doc extends ObservableV2 { t._length = type._length this.share.set(name, t) t._integrate(this, null) - return /** @type {InstanceType} */ (t) + return /** @type {InstanceType} */ (t) } else { throw new Error(`Type with the name ${name} has already been defined with a different constructor`) } } - return /** @type {InstanceType} */ (type) + return /** @type {InstanceType} */ (type) } /** @@ -261,7 +269,7 @@ export class Doc extends ObservableV2 { * @public */ getArray (name = '') { - return /** @type {YArray} */ (this.get(name, YArray)) + return /** @type {YArray} */ (this.get(name, YArray)) } /** diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js deleted file mode 100644 index d4067fde1..000000000 --- a/src/utils/PermanentUserData.js +++ /dev/null @@ -1,140 +0,0 @@ -import { - YArray, - YMap, - readIdSet, - writeIdSet, - createIdSet, - mergeIdSets, - IdSetEncoderV1, DSDecoderV1, ID, IdSet, YArrayEvent, Transaction, Doc // eslint-disable-line -} from '../internals.js' - -import * as decoding from 'lib0/decoding' - -export class PermanentUserData { - /** - * @param {Doc} doc - * @param {YMap} [storeType] - */ - constructor (doc, storeType = doc.getMap('users')) { - /** - * @type {Map} - */ - const dss = new Map() - this.yusers = storeType - this.doc = doc - /** - * Maps from clientid to userDescription - * - * @type {Map} - */ - this.clients = new Map() - this.dss = dss - /** - * @param {YMap} user - * @param {string} userDescription - */ - const initUser = (user, userDescription) => { - /** - * @type {YArray} - */ - const ds = user.get('ds') - const ids = user.get('ids') - const addClientId = /** @param {number} clientid */ clientid => this.clients.set(clientid, userDescription) - ds.observe(/** @param {YArrayEvent} event */ event => { - event.changes.added.forEach(item => { - item.content.getContent().forEach(encodedDs => { - if (encodedDs instanceof Uint8Array) { - this.dss.set(userDescription, mergeIdSets([this.dss.get(userDescription) || createIdSet(), readIdSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))])) - } - }) - }) - }) - this.dss.set(userDescription, mergeIdSets(ds.map(encodedDs => readIdSet(new DSDecoderV1(decoding.createDecoder(encodedDs)))))) - ids.observe(/** @param {YArrayEvent} event */ event => - event.changes.added.forEach(item => item.content.getContent().forEach(addClientId)) - ) - ids.forEach(addClientId) - } - // observe users - storeType.observe(event => { - event.keysChanged.forEach(userDescription => - initUser(storeType.get(userDescription), userDescription) - ) - }) - // add initial data - storeType.forEach(initUser) - } - - /** - * @param {Doc} doc - * @param {number} clientid - * @param {string} userDescription - * @param {Object} conf - * @param {function(Transaction, IdSet):boolean} [conf.filter] - */ - setUserMapping (doc, clientid, userDescription, { filter = () => true } = {}) { - const users = this.yusers - let user = users.get(userDescription) - if (!user) { - user = new YMap() - user.set('ids', new YArray()) - user.set('ds', new YArray()) - users.set(userDescription, user) - } - user.get('ids').push([clientid]) - users.observe(_event => { - 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 = new IdSetEncoderV1() - const ds = this.dss.get(userDescription) - if (ds) { - writeIdSet(encoder, ds) - user.get('ds').push([encoder.toUint8Array()]) - } - } - }, 0) - }) - doc.on('afterTransaction', /** @param {Transaction} transaction */ transaction => { - setTimeout(() => { - const yds = user.get('ds') - const ds = transaction.deleteSet - if (transaction.local && ds.clients.size > 0 && filter(transaction, ds)) { - const encoder = new IdSetEncoderV1() - writeIdSet(encoder, ds) - yds.push([encoder.toUint8Array()]) - } - }) - }) - } - - /** - * @param {number} clientid - * @return {any} - */ - getUserByClientId (clientid) { - return this.clients.get(clientid) || null - } - - /** - * @param {ID} id - * @return {string | null} - */ - getUserByDeletedId (id) { - for (const [userDescription, ds] of this.dss.entries()) { - if (ds.hasId(id)) { - return userDescription - } - } - return null - } -} diff --git a/src/utils/RelativePosition.js b/src/utils/RelativePosition.js index ddee880c4..c89aa2660 100644 --- a/src/utils/RelativePosition.js +++ b/src/utils/RelativePosition.js @@ -153,7 +153,7 @@ export const createRelativePosition = (type, item, assoc) => { /** * Create a relativePosition based on a absolute position. * - * @param {AbstractType} type The base type (e.g. YText or YArray). + * @param {AbstractType} type The base type (e.g. YText or YArray). * @param {number} index The absolute position. * @param {number} [assoc] * @param {import('../utils/AttributionManager.js').AbstractAttributionManager} attributionManager diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index f6b59095b..ea34e5719 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -84,13 +84,13 @@ 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) - * @type {Map>,Set>} + * @type {Map>} */ this.changed = new Map() /** * Stores the events for the types that observe also child elements. * It is mainly used by `observeDeep`. - * @type {Map>,Array>>} + * @type {Map>>} */ this.changedParentTypes = new Map() /** @@ -198,7 +198,7 @@ export const nextID = transaction => { * did not change, it was just added and we should not fire events for `type`. * * @param {Transaction} transaction - * @param {AbstractType>} type + * @param {import('../utils/types.js').YType} type * @param {string|null} parentSub */ export const addChangedTypeToTransaction = (transaction, type, parentSub) => { diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 09cdc4b74..6857eb2f5 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -37,7 +37,7 @@ export class StackItem { */ const clearUndoManagerStackItem = (tr, um, stackItem) => { iterateStructsByIdSet(tr, stackItem.deletions, item => { - if (item instanceof Item && um.scope.some(type => type === tr.doc || isParentOf(/** @type {AbstractType} */ (type), item))) { + if (item instanceof Item && um.scope.some(type => type === tr.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), item))) { keepItem(item, false) } }) @@ -79,7 +79,7 @@ const popStackItem = (undoManager, stack, eventType) => { } struct = item } - if (!struct.deleted && scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType} */ (type), /** @type {Item} */ (struct)))) { + if (!struct.deleted && scope.some(type => type === transaction.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), /** @type {Item} */ (struct)))) { itemsToDelete.push(struct) } } @@ -87,7 +87,7 @@ const popStackItem = (undoManager, stack, eventType) => { iterateStructsByIdSet(transaction, stackItem.deletions, struct => { if ( struct instanceof Item && - scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType} */ (type), struct)) && + scope.some(type => type === transaction.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), struct)) && // Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval. !stackItem.insertions.hasId(struct.id) ) { @@ -143,7 +143,7 @@ const popStackItem = (undoManager, stack, eventType) => { * @property {StackItem} StackItemEvent.stackItem * @property {any} StackItemEvent.origin * @property {'undo'|'redo'} StackItemEvent.type - * @property {Map>,Array>>} StackItemEvent.changedParentTypes + * @property {Map>>} StackItemEvent.changedParentTypes */ /** @@ -157,7 +157,7 @@ const popStackItem = (undoManager, stack, eventType) => { */ export class UndoManager extends ObservableV2 { /** - * @param {Doc|AbstractType|Array>} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types. + * @param {Doc|import('../utils/types.js').YType|Array} typeScope Limits the scope of the UndoManager. If this is set to a ydoc instance, all changes on that ydoc will be undone. If set to a specific type, only changes on that type or its children will be undone. Also accepts an array of types. * @param {UndoManagerOptions} options */ constructor (typeScope, { @@ -170,7 +170,7 @@ export class UndoManager extends ObservableV2 { } = {}) { super() /** - * @type {Array | Doc>} + * @type {Array} */ this.scope = [] this.doc = doc @@ -210,7 +210,7 @@ export class UndoManager extends ObservableV2 { // Only track certain transactions if ( !this.captureTransaction(transaction) || - !this.scope.some(type => transaction.changedParentTypes.has(/** @type {AbstractType} */ (type)) || type === this.doc) || + !this.scope.some(type => transaction.changedParentTypes.has(/** @type {import('../utils/types.js').YType} */ (type)) || type === this.doc) || (!this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor))) ) { return @@ -242,7 +242,7 @@ export class UndoManager extends ObservableV2 { } // make sure that deleted structs are not gc'd iterateStructsByIdSet(transaction, transaction.deleteSet, /** @param {Item|GC} item */ item => { - if (item instanceof Item && this.scope.some(type => type === transaction.doc || isParentOf(/** @type {AbstractType} */ (type), item))) { + if (item instanceof Item && this.scope.some(type => type === transaction.doc || isParentOf(/** @type {import('../utils/types.js').YType} */ (type), item))) { keepItem(item, true) } }) @@ -265,7 +265,7 @@ export class UndoManager extends ObservableV2 { /** * Extend the scope. * - * @param {Array | Doc> | AbstractType | Doc} ytypes + * @param {Array | import('../utils/types.js').YType | Doc} ytypes */ addToScope (ytypes) { const tmpSet = new Set(this.scope) diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 37c32eae7..082944dd7 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -1,31 +1,39 @@ import { - TextDeltaBuilder, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line + diffIdSet, + mergeIdSets, + noAttributionsManager, + AbstractAttributionManager, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line } from '../internals.js' -import * as set from 'lib0/set' import * as array from 'lib0/array' import * as error from 'lib0/error' +import * as delta from 'lib0/delta' // eslint-disable-line + +/** + * @typedef {import('./types.js').YType} _YType + */ const errorComputeChanges = 'You must not compute changes after the event-handler fired.' /** - * @template {AbstractType} T + * @template {_YType} Target * YEvent describes the changes on a YType. */ export class YEvent { /** - * @param {T} target The changed type. + * @param {Target} target The changed type. * @param {Transaction} transaction + * @param {Set?} subs The keys that changed */ - constructor (target, transaction) { + constructor (target, transaction, subs) { /** * The type on which this event was created on. - * @type {T} + * @type {Target} */ this.target = target /** * The current target on which the observe callback is called. - * @type {AbstractType} + * @type {_YType} */ this.currentTarget = target /** @@ -42,13 +50,31 @@ export class YEvent { */ this._keys = null /** - * @type {import('./Delta.js').TextDelta?} + * @type {(Target extends AbstractType ? D : delta.Delta)|null} */ this._delta = null /** * @type {Array|null} */ this._path = null + /** + * Whether the children changed. + * @type {Boolean} + * @private + */ + this.childListChanged = false + /** + * Set of all changed attributes. + * @type {Set} + */ + this.keysChanged = new Set() + subs?.forEach((sub) => { + if (sub === null) { + this.childListChanged = true + } else { + this.keysChanged.add(sub) + } + }) } /** @@ -90,6 +116,7 @@ export class YEvent { } const keys = new Map() const target = this.target + // @ts-ignore const changed = /** @type Set */ (this.transaction.changed.get(target)) changed.forEach(key => { if (key !== null) { @@ -136,18 +163,6 @@ export class YEvent { return this._keys } - /** - * This is a computed property. Note that this can only be safely computed during the - * event call. Computing this property after other changes happened might result in - * unexpected behavior (incorrect computation of deltas). A safe way to collect changes - * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. - * - * @type {import('./Delta.js').Delta} - */ - get delta () { - return this.changes.delta - } - /** * Check if a struct is added by this event. * @@ -161,77 +176,25 @@ export class YEvent { } /** - * This is a computed property. Note that this can only be safely computed during the - * event call. Computing this property after other changes happened might result in - * unexpected behavior (incorrect computation of deltas). A safe way to collect changes - * is to store the `changes` or the `delta` object. Avoid storing the `transaction` object. + * @param {AbstractAttributionManager} am + * @return {Target extends AbstractType ? D : delta.Delta} The Delta representation of this type. * - * @type {{added:Set,deleted:Set,keys:Map,delta:import('./Delta.js').Delta}} + * @public */ - get changes () { - let changes = this._changes - if (changes === null) { - if (this.transaction.doc._transactionCleanups.length === 0) { - throw error.create(errorComputeChanges) - } - const target = this.target - const added = set.create() - const deleted = set.create() - /** - * @type {Array<{insert:Array}|{delete:number}|{retain:number}>} - */ - const delta = [] - changes = { - added, - deleted, - delta, - keys: this.keys - } - const changed = /** @type Set */ (this.transaction.changed.get(target)) - if (changed.has(null)) { - /** - * @type {any} - */ - let lastOp = null - const packOp = () => { - if (lastOp) { - delta.push(lastOp) - } - } - for (let item = target._start; item !== null; item = item.right) { - if (item.deleted) { - if (this.deletes(item) && !this.adds(item)) { - if (lastOp === null || lastOp.delete === undefined) { - packOp() - lastOp = { delete: 0 } - } - lastOp.delete += item.length - deleted.add(item) - } // else nop - } else { - if (this.adds(item)) { - if (lastOp === null || lastOp.insert === undefined) { - packOp() - lastOp = { insert: [] } - } - lastOp.insert = lastOp.insert.concat(item.content.getContent()) - added.add(item) - } else { - if (lastOp === null || lastOp.retain === undefined) { - packOp() - lastOp = { retain: 0 } - } - lastOp.retain += item.length - } - } - } - if (lastOp !== null && lastOp.retain === undefined) { - packOp() - } - } - this._changes = changes - } - return /** @type {any} */ (changes) + getDelta (am = noAttributionsManager) { + const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) + return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged })) + } + + /** + * Compute the changes in the delta format. + * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. + * + * @type {Target extends AbstractType ? D : delta.Delta} The Delta representation of this type. + * @public + */ + get delta () { + return /** @type {any} */ (this._delta ?? (this._delta = this.getDelta())) } } @@ -245,8 +208,8 @@ export class YEvent { * console.log(path) // might look like => [2, 'key1'] * child === type.get(path[0]).get(path[1]) * - * @param {AbstractType} parent - * @param {AbstractType} child target + * @param {_YType} parent + * @param {_YType} child target * @return {Array} Path to the target * * @private @@ -261,7 +224,7 @@ const getPathTo = (parent, child) => { } else { // parent is array-ish let i = 0 - let c = /** @type {AbstractType} */ (child._item.parent)._start + let c = /** @type {import('../utils/types.js').YType} */ (child._item.parent)._start while (c !== child._item && c !== null) { if (!c.deleted && c.countable) { i += c.length @@ -270,7 +233,7 @@ const getPathTo = (parent, child) => { } path.unshift(i) } - child = /** @type {AbstractType} */ (child._item.parent) + child = /** @type {_YType} */ (child._item.parent) } return path } diff --git a/src/utils/encoding.js b/src/utils/encoding.js index 285e82598..d8bf4e878 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -501,6 +501,9 @@ export const writeStateAsUpdate = (encoder, doc, targetStateVector = new Map()) export const encodeStateAsUpdateV2 = (doc, encodedTargetStateVector = new Uint8Array([0]), encoder = new UpdateEncoderV2()) => { const targetStateVector = decodeStateVector(encodedTargetStateVector) writeStateAsUpdate(encoder, doc, targetStateVector) + /** + * @type {Uint8Array[]} + */ const updates = [encoder.toUint8Array()] // also add the pending updates (if there are any) if (doc.store.pendingDs) { diff --git a/src/utils/isParentOf.js b/src/utils/isParentOf.js index d8f5a613b..e26d4eb10 100644 --- a/src/utils/isParentOf.js +++ b/src/utils/isParentOf.js @@ -3,7 +3,7 @@ import { AbstractType, Item } from '../internals.js' // eslint-disable-line /** * Check if `parent` is a parent of `child`. * - * @param {AbstractType} parent + * @param {import('../utils/types.js').YType} parent * @param {Item|null} child * @return {Boolean} Whether `parent` is a parent of `child`. * diff --git a/src/utils/types.js b/src/utils/types.js index 0901a6b6c..a04140785 100644 --- a/src/utils/types.js +++ b/src/utils/types.js @@ -5,7 +5,27 @@ * |import('../index.js').Map * |import('../index.js').Text * |import('../index.js').XmlElement - * |import('../index.js').XmlFragment + * |import('../index.js').XmlFragment * |import('../index.js').XmlText * |import('../index.js').XmlHook} YValue */ + +/** + * @typedef {import('../types/YArray.js').YArray + * | import('../types/YMap.js').YMap + * | import('../types/YText.js').YText + * | import('../types/YXmlFragment.js').YXmlFragment + * | import('../types/YXmlElement.js').YXmlElement + * | import('../types/YXmlHook.js').YXmlHook + * | import('../types/YXmlText.js').YXmlText} YType + */ + +/** + * @typedef {typeof import('../types/YArray.js').YArray + * | typeof import('../types/YMap.js').YMap + * | typeof import('../types/YText.js').YText + * | typeof import('../types/YXmlFragment.js').YXmlFragment + * | typeof import('../types/YXmlElement.js').YXmlElement + * | typeof import('../types/YXmlHook.js').YXmlHook + * | typeof import('../types/YXmlText.js').YXmlText} YTypeConstructors + */ diff --git a/tests/attribution.tests.js b/tests/attribution.tests.js index 13bc896a0..d44d26cea 100644 --- a/tests/attribution.tests.js +++ b/tests/attribution.tests.js @@ -7,7 +7,7 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' -import * as delta from '../src/utils/Delta.js' +import * as delta from 'lib0/delta' /** * @param {t.TestCase} _tc @@ -40,11 +40,11 @@ export const testAttributedEvents = _tc => { }) const am = Y.createAttributionManagerFromDiff(v1, ydoc) const c1 = ytext.getContent(am) - t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) + t.compare(c1, delta.text().insert('hello ').insert('world', null, { delete: [] })) let calledObserver = false ytext.observe(event => { const d = event.getDelta(am) - t.compare(d, delta.createTextDelta().retain(11).insert('!', null, { insert: [] })) + t.compare(d, delta.text().retain(11).insert('!', null, { insert: [] })) calledObserver = true }) ytext.insert(11, '!') @@ -64,8 +64,8 @@ export const testInsertionsMindingAttributedContent = _tc => { }) const am = Y.createAttributionManagerFromDiff(v1, ydoc) const c1 = ytext.getContent(am) - t.compare(c1, delta.createTextDelta().insert('hello ').insert('world', null, { delete: [] })) - ytext.applyDelta(delta.createTextDelta().retain(11).insert('content'), am) + t.compare(c1, delta.text().insert('hello ').insert('world', null, { delete: [] })) + ytext.applyDelta(delta.text().retain(11).insert('content'), am) t.assert(ytext.toString() === 'hello content') } @@ -82,7 +82,7 @@ export const testInsertionsIntoAttributedContent = _tc => { }) const am = Y.createAttributionManagerFromDiff(v1, ydoc) const c1 = ytext.getContent(am) - t.compare(c1, delta.createTextDelta().insert('hello ').insert('word', null, { insert: [] })) - ytext.applyDelta(delta.createTextDelta().retain(9).insert('l'), am) + t.compare(c1, delta.text().insert('hello ').insert('word', null, { insert: [] })) + ytext.applyDelta(delta.text().retain(9).insert('l'), am) t.assert(ytext.toString() === 'hello world') } diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 1155818e8..513d347f2 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -38,8 +38,8 @@ export const testMapDecodingCompatibilityV1 = _tc => { export const testTextDecodingCompatibilityV1 = _tc => { const oldDoc = 'BS8EAAUBBHRleHRveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9RAQAATHBBAEEAAHBBAIEAAHEBAMEAAQxdXUKxQQCBANveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAlveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xgMBAwIGaXRhbGljBHRydWXGBAsDAgVjb2xvcgYiIzg4OCLEBAwDAgExxAQNAwIBMsEEDgMCAsYEEAMCBml0YWxpYwRudWxsxgQRAwIFY29sb3IEbnVsbMQDAQQLATHEBBMECwIyOcQEFQQLCzl6anpueXdvaHB4xAQgBAsIY25icmNhcQrBAxADEQHGAR8BIARib2xkBHRydWXGAgACAQRib2xkBG51bGzFAwkECm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GARABEQZpdGFsaWMEdHJ1ZcYELQERBWNvbG9yBiIjODg4IsYBEgETBml0YWxpYwRudWxsxgQvARMFY29sb3IEbnVsbMYCKwIsBGJvbGQEdHJ1ZcYCLQIuBGJvbGQEbnVsbMYCjAECjQEGaXRhbGljBHRydWXGAo4BAo8BBml0YWxpYwRudWxswQA2ADcBxgQ1ADcFY29sb3IGIiM4ODgixgNlA2YFY29sb3IEbnVsbMYDUwNUBGJvbGQEdHJ1ZcQEOANUFjEzMTZ6bHBrbWN0b3FvbWdmdGhicGfGBE4DVARib2xkBG51bGzGAk0CTgZpdGFsaWMEdHJ1ZcYEUAJOBWNvbG9yBiIjODg4IsYCTwJQBml0YWxpYwRudWxsxgRSAlAFY29sb3IEbnVsbMYChAEChQEGaXRhbGljBHRydWXGBFQChQEFY29sb3IGIiM4ODgixgKGAQKHAQZpdGFsaWMEbnVsbMYEVgKHAQVjb2xvcgRudWxsxAMpAyoRMTMyMWFwZ2l2eWRxc2pmc2XFBBIDAm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn0zAwAEAQR0ZXh0AjEyhAMBAzkwboQDBAF4gQMFAoQDBwJyCsQDBAMFBjEyOTd6bcQDDwMFAXbEAxADBQFwwQMRAwUBxAMSAwUFa3pxY2rEAxcDBQJzYcQDGQMFBHNqeQrBAxIDEwHBAAwAEAHEAA0ADgkxMzAyeGNpd2HEAygADgF5xAMpAA4KaGhlenVraXF0dMQDMwAOBWhudGsKxgMoAykEYm9sZAR0cnVlxAM5AykGMTMwNXJswQM/AykCxANBAykDZXlrxgNEAykEYm9sZARudWxsxAMzAzQJMTMwN3R2amllwQNOAzQCxANQAzQDamxoxANTAzQCZ3bEA1UDNAJsYsQDVwM0AmYKxgNBA0IEYm9sZARudWxswQNaA0ICxANcA0ICMDjBA14DQgLEA2ADQgEKxgNhA0IEYm9sZAR0cnVlxQIaAhtveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9wQA3ADgCwQNlADgBxANmADgKMTVteml3YWJ6a8EDcAA4AsQDcgA4BnJybXNjdsEDeAA4AcQCYgJjATHEA3oCYwIzMsQDfAJjCTRyb3J5d3RoccQDhQECYwEKxAOFAQOGARkxMzI1aW9kYnppenhobWxpYnZweXJ4bXEKwQN6A3sBxgOgAQN7BWNvbG9yBiIjODg4IsYDfAN9Bml0YWxpYwRudWxsxgOiAQN9BWNvbG9yBG51bGxSAgAEAQR0ZXh0ATGEAgACMjiEAgIBOYECAwKEAgUBdYQCBgJ0Y4QCCAJqZYECCgKEAgwBaoECDQGBAg4BhAIPAnVmhAIRAQrEAg4CDwgxMjkycXJtZsQCGgIPAmsKxgIGAgcGaXRhbGljBHRydWXGAggCCQZpdGFsaWMEbnVsbMYCEQISBml0YWxpYwR0cnVlxAIfAhIBMcECIAISAsQCIgISAzRoc8QCJQISAXrGAiYCEgZpdGFsaWMEbnVsbMEAFQAWAsQCKQAWATDEAioAFgEwxAIrABYCaHjEAi0AFglvamVldHJqaHjBAjYAFgLEAjgAFgJrcsQCOgAWAXHBAjsAFgHBAjwAFgHEAj0AFgFuxAI+ABYCZQrGAiUCJgZpdGFsaWMEbnVsbMQCQQImAjEzwQJDAiYCxAJFAiYIZGNjeGR5eGfEAk0CJgJ6Y8QCTwImA2Fwb8QCUgImAnRuxAJUAiYBcsQCVQImAmduwQJXAiYCxAJZAiYBCsYCWgImBml0YWxpYwR0cnVlxAI6AjsEMTMwM8QCXwI7A3VodsQCYgI7BmdhbmxuCsUCVQJWb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcECPAI9AcECPgI/AcYDFwMYBml0YWxpYwR0cnVlxgJsAxgFY29sb3IGIiM4ODgixgMZAxoGaXRhbGljBG51bGzGAm4DGgVjb2xvcgRudWxswQMQBCkBxAJwBCkKMTMwOXpsZ3ZqeMQCegQpAWfBAnsEKQLGBA0EDgZpdGFsaWMEbnVsbMYCfgQOBWNvbG9yBG51bGzEAn8EDgUxMzEwZ8QChAEEDgJ3c8QChgEEDgZoeHd5Y2jEAowBBA4Ca3HEAo4BBA4Ec2RydcQCkgEEDgRqcWljwQKWAQQOBMQCmgEEDgEKxgKbAQQOBml0YWxpYwR0cnVlxgKcAQQOBWNvbG9yBiIjODg4IsECaAI7AcQCCgEBFjEzMThqd3NramFiZG5kcmRsbWphZQrGA1UDVgRib2xkBHRydWXGA1cDWARib2xkBG51bGzGAEAAQQZpdGFsaWMEdHJ1ZcYCtwEAQQRib2xkBG51bGzEArgBAEESMTMyNnJwY3pucWFob3BjcnRkxgLKAQBBBml0YWxpYwRudWxsxgLLAQBBBGJvbGQEdHJ1ZRkBAMUCAgIDb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcQCCgILBzEyOTN0agrGABgAGQRib2xkBHRydWXGAA0ADgRib2xkBG51bGxEAgAHMTMwNnJ1cMQBEAIAAnVqxAESAgANaWtrY2pucmNwc2Nrd8QBHwIAAQrFBBMEFG97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3FAx0DBW97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAlICUwRib2xkBHRydWXGAlQCVQRib2xkBG51bGzGAnsCfAZpdGFsaWMEdHJ1ZcYBJQJ8BWNvbG9yBiIjODg4IsYBJgJ8BGJvbGQEbnVsbMQBJwJ8CjEzMTRweWNhdnXGATECfAZpdGFsaWMEbnVsbMYBMgJ8BWNvbG9yBG51bGzBATMCfAHFADEAMm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GADUANgZpdGFsaWMEdHJ1ZcEANwA4AcQAMgAzEzEzMjJybmJhb2tvcml4ZW52cArEAgUCBhcxMzIzbnVjdnhzcWx6bndsZmF2bXBjCsYDDwMQBGJvbGQEdHJ1ZR0AAMQEAwQEDTEyOTVxZnJ2bHlmYXDEAAwEBAFjxAANBAQCanbBAAwADQHEABAADQEywQARAA0ExAAVAA0DZHZmxAAYAA0BYcYCAwIEBml0YWxpYwR0cnVlwQAaAgQCxAAcAgQEMDRrdcYAIAIEBml0YWxpYwRudWxsxQQgBCFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQJAABZveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xAQVBBYGMTMxMWtrxAIqAisIMTMxMnFyd3TEADECKwFixAAyAisDcnhxxAA1AisBasQANgIrAXjEADcCKwZkb3ZhbwrEAgAEKwMxMzHEAEAEKwkzYXhoa3RoaHXGAnoCewRib2xkBG51bGzFAEoCe297ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAEsCewRib2xkBHRydWXEAl8CYBExMzE3cGZjeWhrc3JrcGt0CsQBHwQqCzEzMTliY2Nna3AKxAKSAQKTARUxMzIwY29oYnZjcmtycGpuZ2RvYwoFBAQCAg8CKQE1AQADEAESBBsCAwsGAhIBHgJAAk8CWwJfAmQDcQJ5AaABAQIOBAILAg4CIQIoAjcCPAJEAlgCagJwAXwClwEEngEBAQI0ATcB' // eslint-disable-next-line - const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"291311kk9zjznywohpx"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"xr\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}}] + const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"291311kk9zjznywohpx"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"xr\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]}] const doc = new Y.Doc() Y.applyUpdate(doc, buffer.fromBase64(oldDoc)) - t.compare(doc.getText('text').getContent().toJSON(), oldVal) + t.compare(doc.getText('text').getContent().toJSON().children, oldVal) } diff --git a/tests/delta.tests.js b/tests/delta.tests.js deleted file mode 100644 index 0e036e764..000000000 --- a/tests/delta.tests.js +++ /dev/null @@ -1,227 +0,0 @@ -import * as t from 'lib0/testing' -import * as delta from '../src/utils/Delta.js' -import * as Y from 'yjs' -import * as schema from 'lib0/schema' - -/** - * @param {t.TestCase} _tc - */ -export const testDelta = _tc => { - const d = delta.createTextDelta().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ insert: ['tester'] }).insert('!').done() - t.compare(d.toJSON(), [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }]) -} - -/** - * @param {t.TestCase} _tc - */ -export const testDeltaMerging = _tc => { - const d = delta.createTextDelta() - .insert('hello') - .insert('world') - .insert(' ', { italic: true }) - .insert({}) - .insert([1]) - .insert([2]) - .done() - t.compare(d.toJSON(), [{ insert: 'helloworld' }, { insert: ' ', attributes: { italic: true } }, { insert: {} }, { insert: [1, 2] }]) -} - -/** - * @param {t.TestCase} _tc - */ -export const testUseAttributes = _tc => { - const d = delta.createTextDelta() - .insert('a') - .updateUsedAttributes('bold', true) - .insert('b') - .insert('c', { bold: 4 }) - .updateUsedAttributes('bold', null) - .insert('d') - .useAttributes({ italic: true }) - .insert('e') - .useAttributes(null) - .insert('f') - .done() - const d2 = delta.createTextDelta() - .insert('a') - .insert('b', { bold: true }) - .insert('c', { bold: 4 }) - .insert('d') - .insert('e', { italic: true }) - .insert('f') - .done() - t.compare(d, d2) -} - -/** - * @param {t.TestCase} _tc - */ -export const testUseAttribution = _tc => { - const d = delta.createTextDelta() - .insert('a') - .updateUsedAttribution('insert', ['me']) - .insert('b') - .insert('c', null, { insert: ['you'] }) - .updateUsedAttribution('insert', null) - .insert('d') - .useAttribution({ insert: ['me'] }) - .insert('e') - .useAttribution(null) - .insert('f') - .done() - const d2 = delta.createTextDelta() - .insert('a') - .insert('b', null, { insert: ['me'] }) - .insert('c', null, { insert: ['you'] }) - .insert('d') - .insert('e', null, { insert: ['me'] }) - .insert('f') - .done() - t.compare(d, d2) -} - -/** - * @param {t.TestCase} _tc - */ -export const testMapDelta = _tc => { - const d = /** @type {delta.MapDeltaBuilder<{ key: string, v: number, over: string }>} */ (delta.createMapDelta()) - d.set('key', 'value') - .useAttribution({ delete: ['me'] }) - .delete('v', 94) - .useAttribution(null) - .set('over', 'andout', 'i existed before') - .done() - t.compare(d.toJSON(), { - key: { type: 'insert', value: 'value', prevValue: undefined, attribution: null }, - v: { type: 'delete', prevValue: 94, attribution: { delete: ['me'] } }, - over: { type: 'insert', value: 'andout', prevValue: 'i existed before', attribution: null } - }) - t.compare(d.origin, null) - t.compare(d.remote, false) - t.compare(d.isDiff, true) - d.forEach((change, key) => { - if (key === 'v') { - t.assert(d.get(key)?.prevValue === 94) // should know that value is number - t.assert(change.prevValue === 94) - } else if (key === 'key') { - t.assert(d.get(key)?.value === 'value') // show know that value is a string - t.assert(change.value === 'value') - } else if (key === 'over') { - t.assert(change.value === 'andout') - } else { - throw new Error() - } - }) - for (const [key, change] of d) { - if (key === 'v') { - t.assert(d.get(key)?.prevValue === 94) - t.assert(change.prevValue === 94) // should know that value is number - } else if (key === 'key') { - t.assert(change.value === 'value') // should know that value is string - } else if (key === 'over') { - t.assert(change.value === 'andout') - } else { - throw new Error() - } - } -} - -/** - * @param {t.TestCase} _tc - */ -export const testXmlDelta = _tc => { - const d = /** @type {delta.XmlDelta} */ (delta.createXmlDelta()) - d.children.insert(['hi']) - d.attributes.set('a', 1) - d.attributes.delete('a', 1) - /** - * @type {Array| number>} - */ - const arr = [] - d.children.forEach( - (op, index) => { - if (op instanceof delta.InsertArrayOp) { - arr.push(op.insert, index) - } - }, - (op, index) => { - arr.push(op.insert, index) - }, - (op, _index) => { - arr.push(op.retain) - }, - (op, _index) => { - arr.push(op.delete) - } - ) - t.compare(arr, [['hi'], 0, ['hi'], 0]) - const x = d.done() - console.log(x) -} - -const textDeltaSchema = schema.object({ - ops: schema.array( - schema.any - ) -}) - -/** - * @param {t.TestCase} _tc - */ -export const testTextModifyingDelta = _tc => { - const d = /** @type {delta.TextDelta|Y.Array,undefined>} */ (delta.createTextDelta().insert('hi').insert(new Y.Map()).done()) - schema.assert(d, textDeltaSchema) - console.log(d) -} - -/** - * @param {t.TestCase} _tc - */ -export const testYtypeDeltaTypings = _tc => { - const ydoc = new Y.Doc({ gc: false }) - { - const yarray = /** @type {Y.Array} */ (ydoc.getArray('numbers')) - const content = yarray.getContent() - content.forEach( - op => { - schema.union( - schema.constructedBy(delta.InsertArrayOp), - schema.constructedBy(delta.RetainOp), - schema.constructedBy(delta.DeleteOp) - ).ensure(op) - }, - op => { - schema.constructedBy(delta.InsertArrayOp).ensure(op) - }, - op => { - schema.constructedBy(delta.RetainOp).ensure(op) - }, - op => { - schema.constructedBy(delta.DeleteOp).ensure(op) - } - ) - const cdeep = yarray.getContentDeep() - cdeep.forEach( - op => { - schema.union( - schema.constructedBy(delta.InsertArrayOp), - schema.constructedBy(delta.RetainOp), - schema.constructedBy(delta.DeleteOp), - schema.constructedBy(delta.ModifyOp) - ).ensure(op) - }, - op => { - schema.constructedBy(delta.InsertArrayOp).ensure(op) - }, - op => { - schema.constructedBy(delta.RetainOp).ensure(op) - }, - op => { - schema.constructedBy(delta.DeleteOp).ensure(op) - }, - op => { - schema.constructedBy(delta.ModifyOp).ensure(op) - } - ) - } -} diff --git a/tests/encoding.tests.js b/tests/encoding.tests.js index 0f6db1ade..1121b0862 100644 --- a/tests/encoding.tests.js +++ b/tests/encoding.tests.js @@ -1,5 +1,4 @@ import * as t from 'lib0/testing' -import * as promise from 'lib0/promise' import { contentRefs, @@ -11,11 +10,7 @@ import { readContentType, readContentFormat, readContentAny, - readContentDoc, - Doc, - PermanentUserData, - encodeStateAsUpdate, - applyUpdate + readContentDoc } from '../src/internals.js' import * as Y from '../src/index.js' @@ -37,34 +32,6 @@ export const testStructReferences = tc => { // contentRefs[10] is reserved for Skip structs } -/** - * There is some custom encoding/decoding happening in PermanentUserData. - * This is why it landed here. - * - * @param {t.TestCase} tc - */ -export const testPermanentUserData = async tc => { - const ydoc1 = new Doc() - const ydoc2 = new Doc() - const pd1 = new PermanentUserData(ydoc1) - const pd2 = new PermanentUserData(ydoc2) - pd1.setUserMapping(ydoc1, ydoc1.clientID, 'user a') - pd2.setUserMapping(ydoc2, ydoc2.clientID, 'user b') - ydoc1.getText().insert(0, 'xhi') - ydoc1.getText().delete(0, 1) - ydoc2.getText().insert(0, 'hxxi') - ydoc2.getText().delete(1, 2) - await promise.wait(10) - applyUpdate(ydoc2, encodeStateAsUpdate(ydoc1)) - applyUpdate(ydoc1, encodeStateAsUpdate(ydoc2)) - - // now sync a third doc with same name as doc1 and then create PermanentUserData - const ydoc3 = new Doc() - applyUpdate(ydoc3, encodeStateAsUpdate(ydoc1)) - const pd3 = new PermanentUserData(ydoc3) - pd3.setUserMapping(ydoc3, ydoc3.clientID, 'user a') -} - /** * Reported here: https://github.com/yjs/yjs/issues/308 * @param {t.TestCase} tc diff --git a/tests/index.js b/tests/index.js index 148b524bb..9bd2894d0 100644 --- a/tests/index.js +++ b/tests/index.js @@ -11,7 +11,6 @@ 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 * as idset from './IdSet.tests.js' import * as idmap from './IdMap.tests.js' import * as attribution from './attribution.tests.js' @@ -25,7 +24,7 @@ if (isBrowser) { } const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, delta, idset, idmap, attribution + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, idset, idmap, attribution } const run = async () => { diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index db2db6b0f..c78b04946 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -2,7 +2,7 @@ import * as Y from './testHelper.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' -import * as delta from '../src/utils/Delta.js' +import * as delta from 'lib0/delta' import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager, createAttributionManagerFromSnapshots } from 'yjs/internals' const { init, compare } = Y @@ -13,22 +13,19 @@ const { init, compare } = Y * @param {t.TestCase} _tc */ export const testDeltaBug = _tc => { - const initialDelta = [{ - attributes: { - 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' - }, - insert: '\n' - }, - { - attributes: { + const initialDelta = delta.create() + .insert('\n', { + attributes: { + 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' + }, + insert: '\n' + }) + .insert('\n\n\n', { 'table-col': { width: '150' } - }, - insert: '\n\n\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea', 'table-cell-line': { rowspan: '1', @@ -40,11 +37,8 @@ export const testDeltaBug = _tc => { cell: 'cell-apba4k', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c', 'table-cell-line': { rowspan: '1', @@ -56,11 +50,8 @@ export const testDeltaBug = _tc => { cell: 'cell-a8qf0r', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6', 'table-cell-line': { rowspan: '1', @@ -72,11 +63,8 @@ export const testDeltaBug = _tc => { cell: 'cell-oi9ikb', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627', 'table-cell-line': { rowspan: '1', @@ -88,11 +76,8 @@ export const testDeltaBug = _tc => { cell: 'cell-dt6ks2', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f', 'table-cell-line': { rowspan: '1', @@ -104,11 +89,8 @@ export const testDeltaBug = _tc => { cell: 'cell-qah2ay', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-468a69b5-9332-450b-9107-381d593de249', 'table-cell-line': { rowspan: '1', @@ -120,11 +102,8 @@ export const testDeltaBug = _tc => { cell: 'cell-fpcz5a', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c', 'table-cell-line': { rowspan: '1', @@ -136,11 +115,8 @@ export const testDeltaBug = _tc => { cell: 'cell-zrhylp', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b', 'table-cell-line': { rowspan: '1', @@ -152,11 +128,8 @@ export const testDeltaBug = _tc => { cell: 'cell-s1q9nt', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484', 'table-cell-line': { rowspan: '1', @@ -168,56 +141,22 @@ export const testDeltaBug = _tc => { cell: 'cell-20b0j9', rowspan: '1', colspan: '1' - }, - insert: '\n' - }, - { - attributes: { + }) + .insert('\n', { 'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3' - }, - insert: '\n' - }, - { - insert: 'Content after table' - }, - { - attributes: { + }) + .insert('Content after table') + .insert('\n', { 'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce' - }, - insert: '\n' - } - ] + }) const ydoc1 = new Y.Doc() const ytext = ydoc1.getText() ytext.applyDelta(initialDelta) - const addingDash = [ - { - retain: 12 - }, - { - insert: '-' - } - ] + const addingDash = delta.create().retain(12).insert('-') ytext.applyDelta(addingDash) - const addingSpace = [ - { - retain: 13 - }, - { - insert: ' ' - } - ] + const addingSpace = delta.create().retain(13).insert(' ') ytext.applyDelta(addingSpace) - const addingList = [ - { - retain: 12 - }, - { - delete: 2 - }, - { - retain: 1, - attributes: { + const addingList = delta.create().retain(12).delete(2).retain(1, { // Clear table line attribute 'table-cell-line': null, // Add list attribute in place of table-cell-line @@ -228,15 +167,10 @@ export const testDeltaBug = _tc => { cell: 'cell-20b0j9', list: 'bullet' } - } - } - ] + }) ytext.applyDelta(addingList) const result = ytext.getContent() - /** - * @type {delta.TextDelta} - */ - const expectedResult = delta.createTextDelta() + const expectedResult = delta.text() .insert('\n', { 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' }) .insert('\n\n\n', { 'table-col': { width: '150' } }) .insert('\n', { @@ -366,7 +300,6 @@ export const testDeltaBug = _tc => { .insert('\n', { 'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce' }) - .done() t.compare(result, expectedResult) } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 9015f9c44..c1a3bffbb 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -1,7 +1,7 @@ import * as Y from '../src/index.js' import { init, compare } from './testHelper.js' import * as t from 'lib0/testing' -import * as delta from '../src/utils/Delta.js' +import * as delta from 'lib0/delta' export const testCustomTypings = () => { const ydoc = new Y.Doc() @@ -99,25 +99,6 @@ export const testEvents = tc => { compare(users) } -/** - * @param {t.TestCase} tc - */ -export const testTreewalker = tc => { - const { users, xml0 } = init(tc, { users: 3 }) - 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')]) - 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') - t.assert(xml0.querySelector('p') === paragraph1, 'querySelector found paragraph1') - compare(users) -} - /** * @param {t.TestCase} _tc */ @@ -125,7 +106,7 @@ export const testYtextAttributes = _tc => { const ydoc = new Y.Doc() const ytext = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) ytext.observe(event => { - t.compare(event.changes.keys.get('test'), { action: 'add', oldValue: undefined }) + t.assert(event.delta.attrs.get('test')?.type === 'insert') }) ytext.setAttribute('test', 42) t.compare(ytext.getAttribute('test'), 42) @@ -201,13 +182,12 @@ export const testClone = _tc => { export const testFormattingBug = _tc => { const ydoc = new Y.Doc() const yxml = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) - const delta = [ - { insert: 'A', attributes: { em: {}, strong: {} } }, - { insert: 'B', attributes: { em: {} } }, - { insert: 'C', attributes: { em: {}, strong: {} } } - ] - yxml.applyDelta(delta) - t.compare(yxml.getContent().toJSON(), delta) + const q = delta.create() + .insert('A', { em: {}, strong: {} }) + .insert('B', { em: {} }) + .insert('C', { em: {}, strong: {} }) + yxml.applyDelta(q) + t.compare(yxml.getContent(), q) } /** @@ -243,11 +223,11 @@ export const testFragmentAttributedContent = _tc => { yfragment.delete(0, 1) yfragment.insert(1, [elem3]) }) - const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + const expectedContent = delta.create().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) const attributedContent = yfragment.getContent(attributionManager) - console.log(attributedContent.children.toJSON()) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON()) + console.log(attributedContent.toJSON()) + t.assert(attributedContent.equals(expectedContent)) + t.compare(elem1.getContent(attributionManager).toJSON(), delta.create().insert('hello', null, { delete: [] }).toJSON()) }) } @@ -272,29 +252,29 @@ export const testElementAttributedContent = _tc => { yelement.insert(1, [elem3]) yelement.setAttribute('key', '42') }) - const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + const expectedContent = delta.create().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) const attributedContent = yelement.getContent(attributionManager) - console.log('children', attributedContent.children.toJSON()) - console.log('attributes', attributedContent.attributes) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + console.log('children', attributedContent.toJSON()) + console.log('attributes', attributedContent) + t.assert(attributedContent.equals(expectedContent)) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { - const expectedContent = delta.createArrayDelta().insert( - [delta.createTextDelta().insert('hello', null, { delete: [] })], + const expectedContent = delta.create().insert( + [delta.text().insert('hello', null, { delete: [] })], null, { delete: [] } - ).insert([delta.createXmlDelta('span')]) + ).insert([delta.create('span')]) .insert([ - delta.createTextDelta().insert('world', null, { insert: [] }) + delta.text().insert('world', null, { insert: [] }) ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) - console.log('attributes', attributedContent.attributes) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes, /** @type {delta.MapDeltaBuilder} */ (delta.createMapDelta()).set('key', '42', undefined, { insert: [] })) - t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) - t.assert(attributedContent.nodeName === 'UNDEFINED') + console.log('attributes', attributedContent.toJSON().attrs) + t.assert(attributedContent.equals(expectedContent)) + t.compare(attributedContent, /** @type {delta.MapDelta} */ (delta.map()).set('key', '42', { insert: [] })) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.assert(attributedContent.name === 'UNDEFINED') }) }) } @@ -316,64 +296,64 @@ export const testElementAttributedContentViaDiffer = _tc => { yelement.setAttribute('key', '42') }) const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) - const expectedContent = delta.createArrayDelta().insert([delta.createTextDelta().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.createTextDelta().insert('world', null, { insert: [] })], null, { insert: [] }) + const expectedContent = delta.create().insert([delta.create().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.create().insert('world', null, { insert: [] })], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', attributedContent.children.toJSON()) - console.log('attributes', attributedContent.attributes) - t.compare(attributedContent.children.toJSON(), expectedContent.toJSON()) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + console.log('children', attributedContent.toJSON().children) + console.log('attributes', attributedContent.toJSON().attrs) + t.compare(attributedContent.toJSON(), expectedContent.toJSON()) + t.assert(attributedContent.equals(expectedContent)) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { - const expectedContent = delta.createArrayDelta().insert( - [delta.createTextDelta().insert('hello')], + const expectedContent = delta.create().insert( + [delta.create().insert('hello')], null, { delete: [] } - ).insert([delta.createXmlDelta('span')]) + ).insert([delta.create('span')]) .insert([ - delta.createTextDelta().insert('world', null, { insert: [] }) + delta.create().insert('world', null, { insert: [] }) ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) - console.log('attributes', attributedContent.attributes) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) - t.assert(attributedContent.nodeName === 'UNDEFINED') + console.log('attributes', attributedContent.toJSON().attrs) + t.assert(attributedContent.equals(expectedContent)) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.assert(attributedContent.name === 'UNDEFINED') }) ydoc.transact(() => { elem3.insert(0, 'big') }) t.group('test getContentDeep after some more updates', () => { t.info('expecting diffingAttributionManager to auto update itself') - const expectedContent = delta.createArrayDelta().insert( - [delta.createTextDelta().insert('hello')], + const expectedContent = delta.create().insert( + [delta.create().insert('hello')], null, { delete: [] } - ).insert([delta.createXmlDelta('span')]) + ).insert([delta.create('span')]) .insert([ - delta.createTextDelta().insert('bigworld', null, { insert: [] }) + delta.create().insert('bigworld', null, { insert: [] }) ], null, { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) - console.log('attributes', attributedContent.attributes) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) - t.assert(attributedContent.nodeName === 'UNDEFINED') + console.log('attributes', attributedContent.toJSON().attrs) + t.assert(attributedContent.equals(expectedContent)) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.assert(attributedContent.name === 'UNDEFINED') }) Y.applyUpdate(ydocV1, Y.encodeStateAsUpdate(ydoc)) t.group('test getContentDeep both docs synced', () => { t.info('expecting diffingAttributionManager to auto update itself') - const expectedContent = delta.createArrayDelta().insert([delta.createXmlDelta('span')]).insert([ - delta.createTextDelta().insert('bigworld') + const expectedContent = delta.create().insert([delta.create('span')]).insert([ + delta.create().insert('bigworld') ]) const attributedContent = yelement.getContentDeep(attributionManager) - console.log('children', JSON.stringify(attributedContent.children.toJSON(), null, 2)) + console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) - console.log('attributes', attributedContent.attributes) - t.assert(attributedContent.children.equals(expectedContent)) - t.compare(attributedContent.attributes.toJSON(), { key: { type: 'insert', prevValue: undefined, value: '42', attribution: null } }) - t.assert(attributedContent.nodeName === 'UNDEFINED') + console.log('attributes', attributedContent.toJSON().attrs) + t.assert(attributedContent.equals(expectedContent)) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42' } }) + t.assert(attributedContent.name === 'UNDEFINED') }) } diff --git a/tsconfig.json b/tsconfig.json index a4f93c9c3..71beb314c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,5 +19,6 @@ "yjs/testHelper": ["./tests/testHelper.js"] } }, - "include": ["./src/**/*.js", "./tests/**/*.js"] + "include": ["./src/**/*.js", "./tests/**/*.js"], + "exclude": ["../lib0/**"] } From c9829b0993b72da3819cd2b24249bcac7d97a1ce Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 21 Oct 2025 16:31:59 +0200 Subject: [PATCH 350/362] fixed most tests for delta v2 migration --- src/types/AbstractType.js | 97 +- src/types/YArray.js | 21 +- src/types/YText.js | 8 +- src/utils/AttributionManager.js | 4 +- src/utils/YEvent.js | 6 +- src/utils/types.js | 29 +- test.html | 2 + tests/doc.tests.js | 4 +- tests/testHelper.js | 4 +- tests/undo-redo.tests.js | 22 +- tests/updates.tests.js | 47 +- tests/y-array.tests.js | 24 +- tests/y-map.tests.js | 51 +- tests/y-text.tests.js | 2181 +++++++++++++------------------ tsconfig.json | 2 +- 15 files changed, 1039 insertions(+), 1463 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index bce269c69..36c9b4a08 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -233,7 +233,7 @@ export const updateMarkerChanges = (searchMarker, index, len) => { /** * Accumulate all (list) children of a type and return them as an Array. * - * @param {AbstractType} t + * @param {import('../utils/types.js').YType} t * @return {Array} */ export const getTypeChildren = t => { @@ -272,7 +272,7 @@ export const callTypeObservers = (type, transaction, event) => { /** * Abstract Yjs Type class * @template {delta.Delta} [EventDelta=delta.Delta] - * @template {YType_} [Self=any] + * @template {AbstractType} [Self=any] */ export class AbstractType { constructor () { @@ -442,18 +442,19 @@ export class AbstractType { * @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender] * @param {boolean} [opts.retainInserts] - if true, retain rendered inserts with attributions * @param {boolean} [opts.retainDeletes] - if true, retain rendered+attributed deletes only - * @param {Set?} [opts.renderAttrs] - if true, retain rendered+attributed deletes only + * @param {Set?} [opts.renderAttrs] - set of attrs to render. if null, render all attributes * @param {boolean} [opts.renderChildren] - if true, retain rendered+attributed deletes only + * @param {import('../utils/IdSet.js').IdSet?} [opts.deletedItems] - used for computing prevItem in attributes * @return {EventDelta} The Delta representation of this type. * * @public */ - getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true } = {}) { + getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null } = {}) { /** * @type {EventDelta} */ const d = /** @type {any} */ (delta.create()) - typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am) + typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deletedItems, itemsToRender) if (renderChildren) { /** * @type {delta.FormattingAttributes} @@ -625,7 +626,7 @@ export class AbstractType { * @type {import('../utils/AttributionManager.js').Attribution} */ const formattingAttribution = object.assign({}, d.usedAttribution) - const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.attributes = object.assign({}, formattingAttribution.attributes ?? {})) + const changedAttributedAttributes = /** @type {{ [key: string]: Array }} */ (formattingAttribution.format = object.assign({}, formattingAttribution.format ?? {})) if (attribution == null || equalAttrs(previousUnattributedAttributes[key], currentAttributes[key] ?? null)) { // an unattributed formatting attribute was found or an attributed formatting // attribute was found that resets to the previous status @@ -635,13 +636,13 @@ export class AbstractType { const by = changedAttributedAttributes[key] = (changedAttributedAttributes[key]?.slice() ?? []) by.push(...((c.deleted ? attribution.delete : attribution.insert) ?? [])) const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt) formattingAttribution.attributedAt = attributedAt + if (attributedAt) formattingAttribution.formatAt = attributedAt } if (object.isEmpty(changedAttributedAttributes)) { d.useAttribution(null) } else if (attribution != null) { const attributedAt = (c.deleted ? attribution.deletedAt : attribution.insertedAt) - if (attributedAt != null) formattingAttribution.attributedAt = attributedAt + if (attributedAt != null) formattingAttribution.formatAt = attributedAt d.useAttribution(formattingAttribution) } } @@ -651,7 +652,7 @@ export class AbstractType { } } } - return d + return /** @type {any} */ (d.done()) } /** @@ -678,7 +679,7 @@ export class AbstractType { op.value = op.value.getContentDeep(am) } }) - return /** @type {any} */ (d) + return /** @type {any} */ (d.done()) } } @@ -768,47 +769,6 @@ export const typeListToArray = type => { return cs } -/** - * @todo this can be removed as this can be replaced by a generic function - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @template {delta.Delta} TypeDelta - * @param {TypeDelta} d - * @param {import('../utils/types.js').YType} type - * @param {import('../internals.js').AbstractAttributionManager} am - * - * @private - * @function - */ -export const typeListGetContent = (d, type, am) => { - type.doc ?? warnPrematureAccess() - /** - * @type {Array>} - */ - const cs = [] - for (let item = type._start; item !== null; cs.length = 0) { - // populate cs - for (; item !== null && cs.length < 50; item = item.right) { - am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) - } - for (let i = 0; i < cs.length; i++) { - const c = cs[i] - const attribution = createAttributionFromAttributionItems(c.attrs, c.deleted) - if (c.content.isCountable()) { - if (c.render || attribution != null) { - d.insert(c.content.getContent(), null, attribution) - } else if (!c.deleted) { - d.retain(c.content.getLength()) - } - } - } - } -} - /** * @param {AbstractType} type * @param {Snapshot} snapshot @@ -1270,13 +1230,13 @@ export const typeMapGetAll = (parent) => { * @param {YType_} parent * @param {Set?} attrsToRender * @param {import('../internals.js').AbstractAttributionManager} am + * @param {import('../utils/IdSet.js').IdSet?} [deletedItems] + * @param {import('../utils/IdSet.js').IdSet?} [itemsToRender] * * @private * @function */ -export const typeMapGetDelta = (d, parent, attrsToRender, am) => { - parent.doc ?? warnPrematureAccess() - +export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, itemsToRender) => { /** * @param {Item} item * @param {string} key @@ -1291,28 +1251,17 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am) => { const c = array.last(content.getContent()) const attribution = createAttributionFromAttributionItems(attrs, deleted) if (deleted) { - d.unset(key, attribution, c) + if (itemsToRender == null || itemsToRender.hasId(item.lastId)) { + d.unset(key, attribution, c) + } } else { - /** - * @type {Array>} - */ - let cs = [] - for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) { - /** - * @type {Array>} - */ - const tmpcs = [] - am.readContent(tmpcs, prevItem.id.client, prevItem.id.clock, prevItem.deleted, prevItem.content, 1) - cs = tmpcs.concat(cs) - if (cs.length === 0 || cs[0].attrs == null) { - cs.splice(0, cs.findIndex(c => c.attrs != null)) - break - } - if (cs.length > 0) { - cs.length = 1 - } + // find prev content + let prevContentItem = item + // this algorithm is problematic. should check all previous content using am.readcontent + for (; prevContentItem.left !== null && deletedItems?.hasId(prevContentItem.left.lastId); prevContentItem = prevContentItem.left) { + // nop } - const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined + const prevValue = (prevContentItem !== item && itemsToRender?.hasId(prevContentItem.lastId)) ? array.last(prevContentItem.content.getContent()) : undefined d.set(key, c, attribution, prevValue) } } diff --git a/src/types/YArray.js b/src/types/YArray.js index 23642c3ba..af4e7b7ba 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -17,7 +17,6 @@ import { callTypeObservers, transact, warnPrematureAccess, - typeListGetContent, typeListSlice, noAttributionsManager, AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line @@ -108,7 +107,7 @@ export class YArray extends AbstractType { */ _callObserver (transaction, parentSubs) { super._callObserver(transaction, parentSubs) - callTypeObservers(this, transaction, new YEvent(this, transaction, null)) + callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs)) } /** @@ -214,24 +213,6 @@ export class YArray extends AbstractType { return super.getContentDeep(am) } - /** - * Render the difference to another ydoc (which can be empty) and highlight the differences with - * attributions. - * - * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the - * attribution `{ isDeleted: true, .. }`. - * - * @param {AbstractAttributionManager} am - * @return {delta.ArrayDelta} The Delta representation of this type. - * - * @public - */ - getContent (am = noAttributionsManager) { - const d = this.change - typeListGetContent(d, this, am) - return d - } - /** * Returns a portion of this YArray into a JavaScript Array selected * from start to end (end not included). diff --git a/src/types/YText.js b/src/types/YText.js index 19fd305d9..bb7fcbf30 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -758,9 +758,11 @@ export class YText extends AbstractType { transact(this.doc, transaction => { const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) for (const op of d.children) { - if (delta.$insertOp.check(op)) { - if (op.insert.length > 0 || typeof op.insert !== 'string') { - insertText(transaction, this, currPos, op.insert, op.format || {}) + if (delta.$textOp.check(op)) { + insertText(transaction, this, currPos, op.insert, op.format || {}) + } else if (delta.$insertOp.check(op)) { + for (let i = 0; i < op.insert.length; i++) { + insertText(transaction, this, currPos, op.insert[i], op.format || {}) } } else if (delta.$retainOp.check(op)) { currPos.formatText(transaction, this, op.retain, op.format || {}) diff --git a/src/utils/AttributionManager.js b/src/utils/AttributionManager.js index 745bc8415..3bfb4278f 100644 --- a/src/utils/AttributionManager.js +++ b/src/utils/AttributionManager.js @@ -38,8 +38,8 @@ export const attributionJsonSchema = s.$object({ insertedAt: s.$number.optional, delete: s.$array(s.$string).optional, deletedAt: s.$number.optional, - attributes: s.$record(s.$string, s.$array(s.$string)).optional, - attributedAt: s.$number.optional + format: s.$record(s.$string, s.$array(s.$string)).optional, + formatAt: s.$number.optional }) /** diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 082944dd7..181d4cfdb 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -41,10 +41,6 @@ export class YEvent { * @type {Transaction} */ this.transaction = transaction - /** - * @type {Object|null} - */ - this._changes = null /** * @type {null | Map} */ @@ -183,7 +179,7 @@ export class YEvent { */ getDelta (am = noAttributionsManager) { const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged })) + return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged, deletedItems: this.transaction.deleteSet })) } /** diff --git a/src/utils/types.js b/src/utils/types.js index a04140785..f18a546af 100644 --- a/src/utils/types.js +++ b/src/utils/types.js @@ -1,15 +1,4 @@ -/** - * @typedef {Object|Array|number|null|string|Uint8Array|BigInt - * |import('../index.js').Array - * |import('../index.js').Map - * |import('../index.js').Text - * |import('../index.js').XmlElement - * |import('../index.js').XmlFragment - * |import('../index.js').XmlText - * |import('../index.js').XmlHook} YValue - */ - /** * @typedef {import('../types/YArray.js').YArray * | import('../types/YMap.js').YMap @@ -17,15 +6,17 @@ * | import('../types/YXmlFragment.js').YXmlFragment * | import('../types/YXmlElement.js').YXmlElement * | import('../types/YXmlHook.js').YXmlHook - * | import('../types/YXmlText.js').YXmlText} YType + * | import('../types/YXmlText.js').YXmlText} YValueType + */ + +/** + * @typedef {Object|Array|number|null|string|Uint8Array|BigInt|YValueType} YValue + */ + +/** + * @typedef {import('../types/AbstractType.js').AbstractType} YType */ /** - * @typedef {typeof import('../types/YArray.js').YArray - * | typeof import('../types/YMap.js').YMap - * | typeof import('../types/YText.js').YText - * | typeof import('../types/YXmlFragment.js').YXmlFragment - * | typeof import('../types/YXmlElement.js').YXmlElement - * | typeof import('../types/YXmlHook.js').YXmlHook - * | typeof import('../types/YXmlText.js').YXmlText} YTypeConstructors + * @typedef {typeof import('../types/AbstractType.js').AbstractType} YTypeConstructors */ diff --git a/test.html b/test.html index 282340e9c..c1dc0dfa8 100644 --- a/test.html +++ b/test.html @@ -35,6 +35,7 @@ "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/delta": "./node_modules/lib0/delta/d2.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", @@ -201,6 +202,7 @@ "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/delta": "./node_modules/lib0/delta/d2.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", diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 1387a43c8..0381a00f5 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -29,7 +29,7 @@ export const testFindTypeInOtherDoc = _tc => { const ydocClone = new Y.Doc() Y.applyUpdate(ydocClone, Y.encodeStateAsUpdate(ydoc)) /** - * @template {Y.AbstractType} Type + * @template {import('../src/utils/types.js').YType} Type * @param {Type} ytype * @param {Y.Doc} otherYdoc * @return {Type} @@ -47,7 +47,7 @@ export const testFindTypeInOtherDoc = _tc => { if (rootKey == null) { throw new Error('type does not exist in other ydoc') } - return /** @type {Type} */ (otherYdoc.get(rootKey, /** @type {typeof Y.AbstractType} */ (ytype.constructor))) + return /** @type {Type} */ (otherYdoc.get(rootKey, /** @type {import('../src/utils/types.js').YTypeConstructors} */ (ytype.constructor))) } else { /** * If it is a sub type, we use the item id to find the history type. diff --git a/tests/testHelper.js b/tests/testHelper.js index 3a55a5533..ad11f0129 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -7,6 +7,8 @@ import * as object from 'lib0/object' import * as map from 'lib0/map' import * as Y from '../src/index.js' import * as math from 'lib0/math' +import * as list from 'lib0/list' +import * as delta from 'lib0/delta' import { createIdSet, createIdMap, addToIdSet, encodeIdMap } from '../src/internals.js' @@ -484,7 +486,7 @@ export const compare = users => { t.compare(userArrayValues[i], userArrayValues[i + 1]) t.compare(userMapValues[i], userMapValues[i + 1]) t.compare(userXmlValues[i], userXmlValues[i + 1]) - t.compare(userTextValues[i].ops.map(/** @param {any} a */ a => typeof a.insert === 'string' ? a.insert : ' ').join('').length, users[i].getText('text').length) + t.compare(list.toArray(userTextValues[i].children).map(a => delta.$textOp.check(a) ? a.insert : ' ').join('').length, users[i].getText('text').length) t.compare(userTextValues[i], userTextValues[i + 1], '', (_constructor, a, b) => { if (a instanceof Y.AbstractType) { t.compare(a.toJSON(), b.toJSON()) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 88812af1e..4bfd49062 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -1,7 +1,7 @@ import * as Y from '../src/index.js' import { init } from './testHelper.js' // eslint-disable-line import * as t from 'lib0/testing' -import * as delta from '../src/utils/Delta.js' +import * as delta from 'lib0/delta' export const testInconsistentFormat = () => { /** @@ -11,7 +11,7 @@ export const testInconsistentFormat = () => { const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText)) content.format(0, 6, { bold: null }) content.format(6, 4, { type: 'text' }) - t.compare(content.getContent(), delta.createTextDelta().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true })) + t.compare(content.getContent(), delta.create().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true })) } const initializeYDoc = () => { const yDoc = new Y.Doc({ gc: false }) @@ -85,11 +85,11 @@ export const testUndoText = tc => { t.assert(text0.toString() === 'bcxyz') // test marks text0.format(1, 3, { bold: true }) - t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) + t.compare(text0.getContent(), delta.create().insert('b').insert('cxy', { bold: true }).insert('z')) undoManager.undo() - t.compare(text0.getContent(), delta.fromJSON([{ insert: 'bcxyz' }])) + t.compare(text0.getContent(), delta.create().insert('bcxyz')) undoManager.redo() - t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])) + t.compare(text0.getContent(), delta.create().insert('b').insert('cxy', { bold: true }).insert('z')) } /** @@ -677,14 +677,10 @@ export const testUndoDeleteTextFormat = _tc => { undoManager.undo() Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc)) - const expect = delta.fromJSON([ - { insert: 'Attack ships ' }, - { - insert: 'on fire', - attributes: { bold: true } - }, - { insert: ' off the shoulder of Orion.' } - ]) + const expect = delta.create() + .insert('Attack ships ') + .insert('on fire', { bold: true }) + .insert(' off the shoulder of Orion.') t.compare(text.getContent(), expect) t.compare(text2.getContent(), expect) } diff --git a/tests/updates.tests.js b/tests/updates.tests.js index f68c19785..1e16f514b 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -5,6 +5,7 @@ import { readStructSet, readIdSet, UpdateDecoderV2, UpdateEncoderV2, writeIdSet import * as encoding from 'lib0/encoding' import * as decoding from 'lib0/decoding' import * as object from 'lib0/object' +import * as delta from 'lib0/delta' /** * @typedef {Object} Enc @@ -126,7 +127,7 @@ export const testKeyEncoding = tc => { const update = Y.encodeStateAsUpdateV2(users[0]) Y.applyUpdateV2(users[1], update) - t.compare(text1.getContent().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }]) + t.compare(text1.getContent().toJSON().children, [{ insert: 'c', format: { italic: true } }, { insert: 'b' }, { insert: 'a', format: { italic: true } }]) compare(users) } @@ -207,7 +208,7 @@ const checkUpdateCases = (ydoc, updates, enc, hasDeletes) => { } const meta = enc.parseUpdateMeta(mergedUpdates) - meta.from.forEach((clock, client) => t.assert(clock === 0)) + meta.from.forEach((clock, _client) => t.assert(clock === 0)) meta.to.forEach((clock, client) => { const structs = /** @type {Array} */ (merged.store.clients.get(client)) const lastStruct = structs[structs.length - 1] @@ -237,10 +238,10 @@ export const testMergeUpdates1 = _tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testMergeUpdates2 = tc => { - encoders.forEach((enc, i) => { +export const testMergeUpdates2 = _tc => { + encoders.forEach((enc, _i) => { t.info(`Using encoder: ${enc.description}`) const ydoc = new Y.Doc({ gc: false }) const updates = /** @type {Array} */ ([]) @@ -257,23 +258,23 @@ export const testMergeUpdates2 = tc => { } /** - * @param {t.TestCase} tc + * @param {t.TestCase} _tc */ -export const testMergePendingUpdates = tc => { +export const testMergePendingUpdates = _tc => { const yDoc = new Y.Doc() /** * @type {Array} */ const serverUpdates = [] - yDoc.on('update', (update, origin, c) => { + yDoc.on('update', (update, _origin, _c) => { serverUpdates.splice(serverUpdates.length, 0, update) }) const yText = yDoc.getText('textBlock') - yText.applyDelta([{ insert: 'r' }]) - yText.applyDelta([{ insert: 'o' }]) - yText.applyDelta([{ insert: 'n' }]) - yText.applyDelta([{ insert: 'e' }]) - yText.applyDelta([{ insert: 'n' }]) + yText.applyDelta(delta.create().insert('r')) + yText.applyDelta(delta.create().insert('o')) + yText.applyDelta(delta.create().insert('n')) + yText.applyDelta(delta.create().insert('e')) + yText.applyDelta(delta.create().insert('n')) const yDoc1 = new Y.Doc() Y.applyUpdate(yDoc1, serverUpdates[0]) @@ -297,8 +298,7 @@ export const testMergePendingUpdates = tc => { const yDoc5 = new Y.Doc() Y.applyUpdate(yDoc5, update4) Y.applyUpdate(yDoc5, serverUpdates[4]) - // @ts-ignore - const _update5 = Y.encodeStateAsUpdate(yDoc5) // eslint-disable-line + Y.encodeStateAsUpdate(yDoc5) const yText5 = yDoc5.getText('textBlock') t.compareStrings(yText5.toString(), 'nenor') @@ -313,7 +313,7 @@ export const testObfuscateUpdates = _tc => { const ymap = ydoc.getMap('map') const yarray = ydoc.getArray('array') // test ytext - ytext.applyDelta([{ insert: 'text', attributes: { bold: true } }, { insert: { href: 'supersecreturl' } }]) + ytext.applyDelta(delta.create().insert('text', { bold: true }).insert([{ href: 'supersecreturl' }])) // test ymap ymap.set('key', 'secret1') ymap.set('key', 'secret2') @@ -330,13 +330,14 @@ export const testObfuscateUpdates = _tc => { const omap = odoc.getMap('map') const oarray = odoc.getArray('array') // test ytext - const delta = /** @type {Array} */ (otext.getContent().toJSON()) - t.assert(delta.length === 2) - t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4) - t.assert(object.length(delta[0].attributes) === 1) - t.assert(!object.hasProperty(delta[0].attributes, 'bold')) - t.assert(object.length(delta[1]) === 1) - t.assert(object.hasProperty(delta[1], 'insert')) + const d = /** @type {any} */ (otext.getContent().toJSON().children) + t.assert(d.length === 2) + const q = d[0] + t.assert(d[0].insert !== 'text' && d[0].insert.length === 4) + t.assert(object.length(d[0].format) === 1) + t.assert(!object.hasProperty(d[0].format, 'bold')) + t.assert(object.length(d[1]) === 1) + t.assert(object.hasProperty(d[1], 'insert')) // test ymap t.assert(omap.size === 1) t.assert(!omap.has('key')) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 7a31f6321..aec28521f 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -4,7 +4,7 @@ import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' import * as env from 'lib0/environment' -import * as delta from '../src/utils/Delta.js' +import * as delta from 'lib0/delta' const isDevMode = env.getVariable('node_env') === 'development' @@ -384,24 +384,22 @@ export const testObservedeepIndexes = _tc => { export const testChangeEvent = tc => { const { array0, users } = init(tc, { users: 2 }) /** - * @type {any} + * @type {delta.Delta} */ - let changes = null + let d = delta.create() array0.observe(e => { - changes = e.changes + d = e.delta }) const newArr = new Y.Array() array0.insert(0, [newArr, 4, 'dtrn']) - t.assert(changes !== null && changes.added.size === 2 && changes.deleted.size === 0) - t.compare(changes.delta, [{ insert: [newArr, 4, 'dtrn'] }]) - changes = null + t.assert(d !== null && d.children.len === 1) + t.compare(d.toJSON().children, [{ insert: [newArr, 4, 'dtrn'] }]) array0.delete(0, 2) - t.assert(changes !== null && changes.added.size === 0 && changes.deleted.size === 2) - t.compare(changes.delta, [{ delete: 2 }]) - changes = null + t.assert(d !== null && d.children.len === 1) + t.compare(d.toJSON().children, [{ delete: 2 }]) array0.insert(1, [0.1]) - t.assert(changes !== null && changes.added.size === 1 && changes.deleted.size === 0) - t.compare(changes.delta, [{ retain: 1 }, { insert: [0.1] }]) + t.assert(d !== null && d.children.len === 2) + t.compare(d.toJSON().children, [{ retain: 1 }, { insert: [0.1] }]) compare(users) } @@ -531,7 +529,7 @@ export const testAttributedContent = _tc => { yarray.delete(0, 1) yarray.insert(1, [42]) }) - const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) + const expectedContent = delta.create().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] }) const attributedContent = yarray.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index db89f5756..c86155af7 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -4,11 +4,12 @@ import { compareIDs, noAttributionsManager, TwosetAttributionManager, - createIdMapFromIdSet, - mapDeltaJsonSchema + createIdMapFromIdSet } from '../src/internals.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' +import * as delta from 'lib0/delta' +import * as s from 'lib0/schema' /** * @param {t.TestCase} _tc @@ -490,45 +491,41 @@ export const testThrowsDeleteEventsOnClear = tc => { export const testChangeEvent = tc => { const { map0, users } = init(tc, { users: 2 }) /** - * @type {any} - */ - let changes = null - /** - * @type {any} + * @type {delta.Delta?} */ - let keyChange = null + let changes = delta.create() map0.observe(e => { - changes = e.changes + changes = e.delta }) map0.set('a', 1) - keyChange = changes.keys.get('a') - t.assert(changes !== null && keyChange.action === 'add' && keyChange.oldValue === undefined) + let keyChange = changes.attrs.get('a') + t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined) map0.set('a', 2) - keyChange = changes.keys.get('a') - t.assert(changes !== null && keyChange.action === 'update' && keyChange.oldValue === 1) + keyChange = changes.attrs.get('a') + t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === 1) users[0].transact(() => { map0.set('a', 3) map0.set('a', 4) }) - keyChange = changes.keys.get('a') - t.assert(changes !== null && keyChange.action === 'update' && keyChange.oldValue === 2) + keyChange = changes.attrs.get('a') + t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === 2) users[0].transact(() => { map0.set('b', 1) map0.set('b', 2) }) - keyChange = changes.keys.get('b') - t.assert(changes !== null && keyChange.action === 'add' && keyChange.oldValue === undefined) + keyChange = changes.attrs.get('b') + t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined) users[0].transact(() => { map0.set('c', 1) map0.delete('c') }) - t.assert(changes !== null && changes.keys.size === 0) + t.assert(changes !== null && changes.attrs.size === 0) users[0].transact(() => { map0.set('d', 1) map0.set('d', 2) }) - keyChange = changes.keys.get('d') - t.assert(changes !== null && keyChange.action === 'add' && keyChange.oldValue === undefined) + keyChange = changes.attrs.get('d') + t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined) compare(users) } @@ -631,24 +628,24 @@ export const testAttributedContent = _tc => { }) t.group('initial value', () => { ymap.set('test', 42) - const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'insert', prevValue: undefined, value: 42, attribution: { insert: [] } } }) + const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'insert', value: 42, attribution: { insert: [] } }) } const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent.toJSON()) - t.compare(expectedContent, attributedContent.toJSON()) + t.compare(expectedContent, attributedContent.toJSON().attrs) }) t.group('overwrite value', () => { ymap.set('test', 'fourtytwo') - const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'insert', prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }) + const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'insert', prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } }) } const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) - t.compare(expectedContent, attributedContent.toJSON()) + t.compare(expectedContent, attributedContent.toJSON().attrs) }) t.group('delete value', () => { ymap.delete('test') - const expectedContent = mapDeltaJsonSchema.ensure({ test: { type: 'delete', prevValue: 'fourtytwo', attribution: { delete: [] } } }) + const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'delete', prevValue: 'fourtytwo', attribution: { delete: [] } }) } const attributedContent = ymap.getContent(attributionManager) - console.log(attributedContent) - t.compare(expectedContent, attributedContent.toJSON()) + console.log(attributedContent.toJSON()) + t.compare(expectedContent, attributedContent.toJSON().attrs) }) } diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index c78b04946..8e5fa34a3 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -3,6 +3,7 @@ import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' import * as delta from 'lib0/delta' +import * as list from 'lib0/list' import { createIdMapFromIdSet, noAttributionsManager, TwosetAttributionManager, createAttributionManagerFromSnapshots } from 'yjs/internals' const { init, compare } = Y @@ -15,10 +16,7 @@ const { init, compare } = Y export const testDeltaBug = _tc => { const initialDelta = delta.create() .insert('\n', { - attributes: { - 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' - }, - insert: '\n' + 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' }) .insert('\n\n\n', { 'table-col': { @@ -157,17 +155,17 @@ export const testDeltaBug = _tc => { const addingSpace = delta.create().retain(13).insert(' ') ytext.applyDelta(addingSpace) const addingList = delta.create().retain(12).delete(2).retain(1, { - // Clear table line attribute - 'table-cell-line': null, - // Add list attribute in place of table-cell-line - list: { - rowspan: '1', - colspan: '1', - row: 'row-pflz90', - cell: 'cell-20b0j9', - list: 'bullet' - } - }) + // Clear table line attribute + 'table-cell-line': null, + // Add list attribute in place of table-cell-line + list: { + rowspan: '1', + colspan: '1', + row: 'row-pflz90', + cell: 'cell-20b0j9', + list: 'bullet' + } + }) ytext.applyDelta(addingList) const result = ytext.getContent() const expectedResult = delta.text() @@ -308,1227 +306,918 @@ export const testDeltaBug = _tc => { * @param {t.TestCase} _tc */ export const testDeltaBug2 = _tc => { - const initialContent = [ - { insert: "Thomas' section" }, - { - insert: '\n', - attributes: { 'block-id': 'block-61ae80ac-a469-4eae-bac9-3b6a2c380118' } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-d265d93f-1cc7-40ee-bb58-8270fca2619f' } - }, - { insert: '123' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-592a7bee-76a3-4e28-9c25-7a84344f8813', - list: { list: 'toggled', 'toggle-id': 'list-66xfft' } - } - }, - { insert: '456' }, - { - insert: '\n', - attributes: { - indent: 1, - 'block-id': 'block-3ee2bd70-b97f-45b2-9115-f1e8910235b1', - list: { list: 'toggled', 'toggle-id': 'list-6vh0t0' } - } - }, - { insert: '789' }, - { - insert: '\n', - attributes: { - indent: 1, - 'block-id': 'block-78150cf3-9bb5-4dea-a6f5-0ce1d2a98b9c', - list: { list: 'toggled', 'toggle-id': 'list-7jr0l2' } - } - }, - { insert: '901' }, - { - insert: '\n', - attributes: { - indent: 1, - 'block-id': 'block-13c6416f-f522-41d5-9fd4-ce4eb1cde5ba', - list: { list: 'toggled', 'toggle-id': 'list-7uk8qu' } - } - }, - { - insert: { - slash_command: { - id: 'doc_94zq-2436', - sessionId: 'nkwc70p2j', - replace: '/' - } - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-8a1d2bb6-23c2-4bcf-af3c-3919ffea1697' } - }, - { insert: '\n\n\n', attributes: { 'table-col': { width: '150' } } }, - { - insert: '\n', - attributes: { - 'block-id': 'block-84ec3ea4-da6a-4e03-b430-0e5f432936a9', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-blmd4s', - cell: 'cell-m0u5za' - }, + const initialContent = delta.create() + .insert("Thomas' section") + .insert('\n', { 'block-id': 'block-61ae80ac-a469-4eae-bac9-3b6a2c380118' }) + .insert('\n', { 'block-id': 'block-d265d93f-1cc7-40ee-bb58-8270fca2619f' }) + .insert('123') + .insert('\n', { + 'block-id': 'block-592a7bee-76a3-4e28-9c25-7a84344f8813', + list: { list: 'toggled', 'toggle-id': 'list-66xfft' } + }) + .insert('456') + .insert('\n', { + indent: 1, + 'block-id': 'block-3ee2bd70-b97f-45b2-9115-f1e8910235b1', + list: { list: 'toggled', 'toggle-id': 'list-6vh0t0' } + }) + .insert('789') + .insert('\n', { + indent: 1, + 'block-id': 'block-78150cf3-9bb5-4dea-a6f5-0ce1d2a98b9c', + list: { list: 'toggled', 'toggle-id': 'list-7jr0l2' } + }) + .insert('901') + .insert('\n', { + indent: 1, + 'block-id': 'block-13c6416f-f522-41d5-9fd4-ce4eb1cde5ba', + list: { list: 'toggled', 'toggle-id': 'list-7uk8qu' } + }) + .insert([{ + slash_command: { + id: 'doc_94zq-2436', + sessionId: 'nkwc70p2j', + replace: '/' + } + }]) + .insert('\n', { 'block-id': 'block-8a1d2bb6-23c2-4bcf-af3c-3919ffea1697' }) + .insert('\n\n\n', { 'table-col': { width: '150' } }) + .insert('\n', { + 'block-id': 'block-84ec3ea4-da6a-4e03-b430-0e5f432936a9', + 'table-cell-line': { + rowspan: '1', + colspan: '1', row: 'row-blmd4s', - cell: 'cell-m0u5za', + cell: 'cell-m0u5za' + }, + row: 'row-blmd4s', + cell: 'cell-m0u5za', + rowspan: '1', + colspan: '1' + } + ) + .insert('\n', { + 'block-id': 'block-83144ca8-aace-401e-8aa5-c05928a8ccf0', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-83144ca8-aace-401e-8aa5-c05928a8ccf0', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-blmd4s', - cell: 'cell-1v8s8t' - }, + colspan: '1', row: 'row-blmd4s', - cell: 'cell-1v8s8t', + cell: 'cell-1v8s8t' + }, + row: 'row-blmd4s', + cell: 'cell-1v8s8t', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-9a493387-d27f-4b58-b2f7-731dfafda32a', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-9a493387-d27f-4b58-b2f7-731dfafda32a', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-blmd4s', - cell: 'cell-126947' - }, + colspan: '1', row: 'row-blmd4s', - cell: 'cell-126947', + cell: 'cell-126947' + }, + row: 'row-blmd4s', + cell: 'cell-126947', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-3484f86e-ae42-440f-8de6-857f0d8011ea', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-3484f86e-ae42-440f-8de6-857f0d8011ea', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-hmmljo', - cell: 'cell-wvutl9' - }, + colspan: '1', row: 'row-hmmljo', - cell: 'cell-wvutl9', + cell: 'cell-wvutl9' + }, + row: 'row-hmmljo', + cell: 'cell-wvutl9', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-d4e0b741-9dea-47a5-85e1-4ded0efbc89d', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d4e0b741-9dea-47a5-85e1-4ded0efbc89d', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-hmmljo', - cell: 'cell-nkablr' - }, + colspan: '1', row: 'row-hmmljo', - cell: 'cell-nkablr', + cell: 'cell-nkablr' + }, + row: 'row-hmmljo', + cell: 'cell-nkablr', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-352f0d5a-d1b9-422f-b136-4bacefd00b1a', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-352f0d5a-d1b9-422f-b136-4bacefd00b1a', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-hmmljo', - cell: 'cell-n8xtd0' - }, + colspan: '1', row: 'row-hmmljo', - cell: 'cell-n8xtd0', + cell: 'cell-n8xtd0' + }, + row: 'row-hmmljo', + cell: 'cell-n8xtd0', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-95823e57-f29c-44cf-a69d-2b4494b7144b', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-95823e57-f29c-44cf-a69d-2b4494b7144b', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-ev4xwq', - cell: 'cell-ua9bvu' - }, + colspan: '1', row: 'row-ev4xwq', - cell: 'cell-ua9bvu', + cell: 'cell-ua9bvu' + }, + row: 'row-ev4xwq', + cell: 'cell-ua9bvu', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-cde5c027-15d3-4780-9e76-1e1a9d97a8e8', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-cde5c027-15d3-4780-9e76-1e1a9d97a8e8', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-ev4xwq', - cell: 'cell-7bwuvk' - }, + colspan: '1', row: 'row-ev4xwq', - cell: 'cell-7bwuvk', + cell: 'cell-7bwuvk' + }, + row: 'row-ev4xwq', + cell: 'cell-7bwuvk', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-11a23ed4-b04d-4e45-8065-8120889cd4a4', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-11a23ed4-b04d-4e45-8065-8120889cd4a4', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-ev4xwq', - cell: 'cell-aouka5' - }, + colspan: '1', row: 'row-ev4xwq', - cell: 'cell-aouka5', - rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-15b4483c-da98-4ded-91d3-c3d6ebc82582' } - }, - { insert: { divider: true } }, - { - insert: '\n', - attributes: { 'block-id': 'block-68552c8e-b57b-4f4a-9f36-6cc1ef6b3461' } - }, - { insert: 'jklasjdf' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-c8b2df7d-8ec5-4dd4-81f1-8d8efc40b1b4', - list: { list: 'toggled', 'toggle-id': 'list-9ss39s' } - } - }, - { insert: 'asdf' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-4f252ceb-14da-49ae-8cbd-69a701d18e2a', - list: { list: 'toggled', 'toggle-id': 'list-uvo013' } - } - }, - { insert: 'adg' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533', - list: { list: 'toggled', 'toggle-id': 'list-k53iwe' } - } - }, - { insert: 'asdfasdfasdf' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533', - list: { list: 'none' }, - indent: 1 - } - }, - { insert: 'asdf' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-f406f76d-f338-4261-abe7-5c9131f7f1ad', - list: { list: 'toggled', 'toggle-id': 'list-en86ur' } - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-be18141c-9b6b-434e-8fd0-2c214437d560' } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-36922db3-4af5-48a1-9ea4-0788b3b5d7cf' } - }, - { insert: { table_content: true } }, - { insert: ' ' }, - { - insert: { - slash_command: { - id: 'doc_94zq-2436', - sessionId: 'hiyrt6fny', - replace: '/' - } - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-4b545085-114d-4d07-844c-789710ec3aab', - layout: + cell: 'cell-aouka5' + }, + row: 'row-ev4xwq', + cell: 'cell-aouka5', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { 'block-id': 'block-15b4483c-da98-4ded-91d3-c3d6ebc82582' }) + .insert([{ divider: true }]) + .insert('\n', { 'block-id': 'block-68552c8e-b57b-4f4a-9f36-6cc1ef6b3461' }) + .insert('jklasjdf') + .insert('\n', { + 'block-id': 'block-c8b2df7d-8ec5-4dd4-81f1-8d8efc40b1b4', + list: { list: 'toggled', 'toggle-id': 'list-9ss39s' } + }) + .insert('asdf') + .insert('\n', { + 'block-id': 'block-4f252ceb-14da-49ae-8cbd-69a701d18e2a', + list: { list: 'toggled', 'toggle-id': 'list-uvo013' } + }) + .insert('adg') + .insert('\n', { + 'block-id': 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533', + list: { list: 'toggled', 'toggle-id': 'list-k53iwe' } + }) + .insert('asdfasdfasdf') + .insert('\n', { + 'block-id': 'block-ccb9b72e-b94d-45a0-aae4-9b0a1961c533', + list: { list: 'none' }, + indent: 1 + }) + .insert('asdf') + .insert('\n', [{ + 'block-id': 'block-f406f76d-f338-4261-abe7-5c9131f7f1ad', + list: { list: 'toggled', 'toggle-id': 'list-en86ur' } + }]) + .insert('\n', { 'block-id': 'block-be18141c-9b6b-434e-8fd0-2c214437d560' }) + .insert('\n', { 'block-id': 'block-36922db3-4af5-48a1-9ea4-0788b3b5d7cf' }) + .insert([{ table_content: true }]) + .insert(' ') + .insert([{ + slash_command: { + id: 'doc_94zq-2436', + sessionId: 'hiyrt6fny', + replace: '/' + } + }]) + .insert('\n', { 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' }) + .insert('\n', { + 'block-id': 'block-4b545085-114d-4d07-844c-789710ec3aab', + layout: '12d887e1-d1a2-4814-a1a3-0c904e950b46_1185cd29-ef1b-45d5-8fda-51a70b704e64', - 'layout-width': '0.25' - } - }, - { - insert: '\n', - attributes: { - - 'block-id': 'block-4d3f2321-33d1-470e-9b7c-d5a683570148', - layout: + 'layout-width': '0.25' + }) + .insert('\n', { + 'block-id': 'block-4d3f2321-33d1-470e-9b7c-d5a683570148', + layout: '12d887e1-d1a2-4814-a1a3-0c904e950b46_75523ea3-c67f-4f5f-a85f-ac7c8fc0a992', - 'layout-width': '0.5' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-4c7ae1e6-758e-470f-8d7c-ae0325e4ee8a', - layout: + 'layout-width': '0.5' + }) + .insert('\n', { + 'block-id': 'block-4c7ae1e6-758e-470f-8d7c-ae0325e4ee8a', + layout: '12d887e1-d1a2-4814-a1a3-0c904e950b46_54c740ef-fd7b-48c6-85aa-c14e1bfc9297', - 'layout-width': '0.25' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-2d6ff0f4-ff00-42b7-a8e2-b816463d8fb5' } - }, - { insert: { divider: true } }, - { - insert: '\n', - attributes: { 'table-col': { width: '150' } } - }, - { insert: '\n', attributes: { 'table-col': { width: '154' } } }, - { - insert: '\n', - attributes: { 'table-col': { width: '150' } } - }, - - { - insert: '\n', - attributes: { - 'block-id': 'block-38545d56-224b-464c-b779-51fcec24dbbf', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-q0qfck', - cell: 'cell-hmapv4' - }, + 'layout-width': '0.25' + }) + .insert('\n', { 'block-id': 'block-2d6ff0f4-ff00-42b7-a8e2-b816463d8fb5' }) + .insert([{ divider: true }]) + .insert('\n', { 'table-col': { width: '150' } }) + .insert('\n', { 'table-col': { width: '154' } }) + .insert('\n', { 'table-col': { width: '150' } }) + .insert('\n', { + 'block-id': 'block-38545d56-224b-464c-b779-51fcec24dbbf', + 'table-cell-line': { + rowspan: '1', + colspan: '1', row: 'row-q0qfck', - cell: 'cell-hmapv4', + cell: 'cell-hmapv4' + }, + row: 'row-q0qfck', + cell: 'cell-hmapv4', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-d413a094-5f52-4fd4-a4aa-00774f6fdb44', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d413a094-5f52-4fd4-a4aa-00774f6fdb44', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-q0qfck', - cell: 'cell-c0czb2' - }, + colspan: '1', row: 'row-q0qfck', - cell: 'cell-c0czb2', + cell: 'cell-c0czb2' + }, + row: 'row-q0qfck', + cell: 'cell-c0czb2', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-ff855cbc-8871-4e0a-9ba7-de0c1c2aa585', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-ff855cbc-8871-4e0a-9ba7-de0c1c2aa585', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-q0qfck', - cell: 'cell-hcpqmm' - }, + colspan: '1', row: 'row-q0qfck', - cell: 'cell-hcpqmm', + cell: 'cell-hcpqmm' + }, + row: 'row-q0qfck', + cell: 'cell-hcpqmm', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-4841e6ee-fef8-4473-bf04-f5ba62db17f0', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-4841e6ee-fef8-4473-bf04-f5ba62db17f0', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-etopyl', - cell: 'cell-0io73v' - }, + colspan: '1', row: 'row-etopyl', - cell: 'cell-0io73v', + cell: 'cell-0io73v' + }, + row: 'row-etopyl', + cell: 'cell-0io73v', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-adeec631-d4fe-4f38-9d5e-e67ba068bd24', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-adeec631-d4fe-4f38-9d5e-e67ba068bd24', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-etopyl', - cell: 'cell-gt2waa' - }, + colspan: '1', row: 'row-etopyl', - cell: 'cell-gt2waa', + cell: 'cell-gt2waa' + }, + row: 'row-etopyl', + cell: 'cell-gt2waa', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-d38a7308-c858-4ce0-b1f3-0f9092384961', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d38a7308-c858-4ce0-b1f3-0f9092384961', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-etopyl', - cell: 'cell-os9ksy' - }, + colspan: '1', row: 'row-etopyl', - cell: 'cell-os9ksy', + cell: 'cell-os9ksy' + }, + row: 'row-etopyl', + cell: 'cell-os9ksy', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-a9df6568-1838-40d1-9d16-3c073b6ce169', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-a9df6568-1838-40d1-9d16-3c073b6ce169', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-hbx9ri' - }, + colspan: '1', row: 'row-0jwjg3', - cell: 'cell-hbx9ri', + cell: 'cell-hbx9ri' + }, + row: 'row-0jwjg3', + cell: 'cell-hbx9ri', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-e26a0cf2-fe62-44a5-a4ca-8678a56d62f1', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-e26a0cf2-fe62-44a5-a4ca-8678a56d62f1', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-yg5m2w' - }, + colspan: '1', row: 'row-0jwjg3', - cell: 'cell-yg5m2w', - rowspan: '1', - colspan: '1' - } - }, - { insert: 'a' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-bfbc5ac2-7417-44b9-9aa5-8e36e4095627', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-yg5m2w' + }, + row: 'row-0jwjg3', + cell: 'cell-yg5m2w', + rowspan: '1', + colspan: '1' + }) + .insert('a') + .insert('\n', { + 'block-id': 'block-bfbc5ac2-7417-44b9-9aa5-8e36e4095627', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { insert: 'b' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-f011c089-6389-47c0-8396-7477a29aa56f', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('b') + .insert('\n', { + 'block-id': 'block-f011c089-6389-47c0-8396-7477a29aa56f', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { insert: 'c' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-4497788d-1e02-4fd5-a80a-48b61a6185cb', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('c') + .insert('\n', { + 'block-id': 'block-4497788d-1e02-4fd5-a80a-48b61a6185cb', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { insert: 'd' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-5d73a2c7-f98b-47c7-a3f5-0d8527962b02', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('d') + .insert('\n', { + 'block-id': 'block-5d73a2c7-f98b-47c7-a3f5-0d8527962b02', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { insert: 'e' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-bfda76ee-ffdd-45db-a22e-a6707e11cf68', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('e') + .insert('\n', { + 'block-id': 'block-bfda76ee-ffdd-45db-a22e-a6707e11cf68', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { insert: 'd' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-35242e64-a69d-4cdb-bd85-2a93766bfab4', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('d') + .insert('\n', { + 'block-id': 'block-35242e64-a69d-4cdb-bd85-2a93766bfab4', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { insert: 'f' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-8baa22c8-491b-4f1b-9502-44179d5ae744', - list: { - rowspan: '1', - colspan: '1', - row: 'row-0jwjg3', - cell: 'cell-1azhl2', - list: 'ordered' - }, + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('f') + .insert('\n', { + 'block-id': 'block-8baa22c8-491b-4f1b-9502-44179d5ae744', + list: { rowspan: '1', colspan: '1', row: 'row-0jwjg3', - cell: 'cell-1azhl2' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-7fa64af0-6974-4205-8cee-529f8bd46852' } - }, - { insert: { divider: true } }, - { insert: "Brandon's Section" }, - { - insert: '\n', - attributes: { - header: 2, - 'block-id': 'block-cf49462c-2370-48ff-969d-576cb32c39a1' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-30ef8361-0dd6-4eee-b4eb-c9012d0e9070' } - }, - { - insert: { - slash_command: { - id: 'doc_94zq-2436', - sessionId: 'x9x08o916', - replace: '/' - } - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-166ed856-cf8c-486a-9365-f499b21d91b3' } - }, - { insert: { divider: true } }, - { - insert: '\n', - attributes: { - row: 'row-kssn15', + cell: 'cell-1azhl2', + list: 'ordered' + }, + rowspan: '1', + colspan: '1', + row: 'row-0jwjg3', + cell: 'cell-1azhl2' + }) + .insert('\n', { 'block-id': 'block-7fa64af0-6974-4205-8cee-529f8bd46852' }) + .insert([{ divider: true }]) + .insert("Brandon's Section") + .insert('\n', { + header: 2, + 'block-id': 'block-cf49462c-2370-48ff-969d-576cb32c39a1' + }) + .insert('\n', { 'block-id': 'block-30ef8361-0dd6-4eee-b4eb-c9012d0e9070' }) + .insert([{ + slash_command: { + id: 'doc_94zq-2436', + sessionId: 'x9x08o916', + replace: '/' + } + }]) + .insert('\n', { 'block-id': 'block-166ed856-cf8c-486a-9365-f499b21d91b3' }) + .insert([{ divider: true }]) + .insert('\n', { + row: 'row-kssn15', + rowspan: '1', + colspan: '1', + 'block-id': 'block-e8079594-4559-4259-98bb-da5280e2a692', + 'table-cell-line': { rowspan: '1', colspan: '1', - 'block-id': 'block-e8079594-4559-4259-98bb-da5280e2a692', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-kssn15', - cell: 'cell-qxbksf' - }, + row: 'row-kssn15', cell: 'cell-qxbksf' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-70132663-14cc-4701-b5c5-eb99e875e2bd', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-kssn15', - cell: 'cell-lsohbx' - }, - cell: 'cell-lsohbx', + }, + cell: 'cell-qxbksf' + }) + .insert('\n', { + 'block-id': 'block-70132663-14cc-4701-b5c5-eb99e875e2bd', + 'table-cell-line': { + rowspan: '1', + colspan: '1', row: 'row-kssn15', + cell: 'cell-lsohbx' + }, + cell: 'cell-lsohbx', + row: 'row-kssn15', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-47a3899c-e3c5-4a7a-a8c4-46e0ae73a4fa', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-47a3899c-e3c5-4a7a-a8c4-46e0ae73a4fa', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-kssn15', - cell: 'cell-hner9k' - }, - cell: 'cell-hner9k', + colspan: '1', row: 'row-kssn15', + cell: 'cell-hner9k' + }, + cell: 'cell-hner9k', + row: 'row-kssn15', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-0f9e650a-7841-412e-b4f2-5571b6d352c2', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-0f9e650a-7841-412e-b4f2-5571b6d352c2', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-juxwc0', - cell: 'cell-ei4yqp' - }, - cell: 'cell-ei4yqp', + colspan: '1', row: 'row-juxwc0', + cell: 'cell-ei4yqp' + }, + cell: 'cell-ei4yqp', + row: 'row-juxwc0', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-53a158a9-8c82-4c82-9d4e-f5298257ca43', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-53a158a9-8c82-4c82-9d4e-f5298257ca43', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-juxwc0', - cell: 'cell-25pf5x' - }, - cell: 'cell-25pf5x', + colspan: '1', row: 'row-juxwc0', + cell: 'cell-25pf5x' + }, + cell: 'cell-25pf5x', + row: 'row-juxwc0', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-da8ba35e-ce6e-4518-8605-c51d781eb07a', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-da8ba35e-ce6e-4518-8605-c51d781eb07a', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-juxwc0', - cell: 'cell-m8reor' - }, - cell: 'cell-m8reor', + colspan: '1', row: 'row-juxwc0', + cell: 'cell-m8reor' + }, + cell: 'cell-m8reor', + row: 'row-juxwc0', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-2dce37c7-2978-4127-bed0-9549781babcb', + 'table-cell-line': { + rowspan: '1', + colspan: '1', + row: 'row-ot4wy5', + cell: 'cell-dinh0i' + }, + cell: 'cell-dinh0i', + row: 'row-ot4wy5', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-7b593f8c-4ea3-44b4-8ad9-4a0abffe759b', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-2dce37c7-2978-4127-bed0-9549781babcb', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-ot4wy5', - cell: 'cell-dinh0i' - }, - cell: 'cell-dinh0i', + colspan: '1', row: 'row-ot4wy5', + cell: 'cell-d115b2' + }, + cell: 'cell-d115b2', + row: 'row-ot4wy5', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-272c28e6-2bde-4477-9d99-ce35b3045895', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-7b593f8c-4ea3-44b4-8ad9-4a0abffe759b', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-ot4wy5', - cell: 'cell-d115b2' - }, - cell: 'cell-d115b2', + colspan: '1', row: 'row-ot4wy5', + cell: 'cell-fuapvo' + }, + cell: 'cell-fuapvo', + row: 'row-ot4wy5', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { 'block-id': 'block-fbf23cab-1ce9-4ede-9953-f2f8250004cf' }) + .insert('\n', { 'block-id': 'block-c3fbb8c9-495c-40b0-b0dd-f6e33dd64b1b' }) + .insert('\n', { 'block-id': 'block-3417ad09-92a3-4a43-b5db-6dbcb0f16db4' }) + .insert('\n', { 'block-id': 'block-b9eacdce-4ba3-4e66-8b69-3eace5656057' }) + .insert('Dan Gornstein') + .insert('\n', { + 'block-id': 'block-d7c6ae0d-a17c-433e-85fd-5efc52b587fb', + header: 1 + }) + .insert('\n', { 'block-id': 'block-814521bd-0e14-4fbf-b332-799c6452a624' }) + .insert('aaa') + .insert('\n', { + 'block-id': 'block-6aaf4dcf-dc21-45c6-b723-afb25fe0f498', + list: { list: 'toggled', 'toggle-id': 'list-idl93b' } + }) + .insert('bb') + .insert('\n', { + indent: 1, + 'block-id': 'block-3dd75392-fa50-4bfb-ba6b-3b7d6bd3f1a1', + list: { list: 'toggled', 'toggle-id': 'list-mrq7j2' } + }) + .insert('ccc') + .insert('\n', { + 'block-id': 'block-2528578b-ecda-4f74-9fd7-8741d72dc8b3', + indent: 2, + list: { list: 'toggled', 'toggle-id': 'list-liu7dl' } + }) + .insert('\n', { 'block-id': 'block-18bf68c3-9ef3-4874-929c-9b6bb1a00325' }) + .insert('\n', { 'table-col': { width: '150' } }) + .insert('\n', { 'table-col': { width: '150' } }) + .insert('\n', { 'table-col': { width: '150' } }) + .insert('\n', { + 'block-id': 'block-d44e74b4-b37f-48e0-b319-6327a6295a57', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-272c28e6-2bde-4477-9d99-ce35b3045895', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-ot4wy5', - cell: 'cell-fuapvo' - }, - cell: 'cell-fuapvo', - row: 'row-ot4wy5', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + }, + row: 'row-si1nah', + cell: 'cell-cpybie', + rowspan: '1', + colspan: '1' + }) + .insert('aaa') + .insert('\n', { + 'block-id': 'block-3e545ee9-0c9a-42d7-a4d0-833edb8087f3', + list: { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-fbf23cab-1ce9-4ede-9953-f2f8250004cf' } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-c3fbb8c9-495c-40b0-b0dd-f6e33dd64b1b' } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-3417ad09-92a3-4a43-b5db-6dbcb0f16db4' } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-b9eacdce-4ba3-4e66-8b69-3eace5656057' } - }, - { insert: 'Dan Gornstein' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d7c6ae0d-a17c-433e-85fd-5efc52b587fb', - header: 1 - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-814521bd-0e14-4fbf-b332-799c6452a624' } - }, - { insert: 'aaa' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-6aaf4dcf-dc21-45c6-b723-afb25fe0f498', - list: { list: 'toggled', 'toggle-id': 'list-idl93b' } - } - }, - { insert: 'bb' }, - { - insert: '\n', - attributes: { - indent: 1, - 'block-id': 'block-3dd75392-fa50-4bfb-ba6b-3b7d6bd3f1a1', - list: { list: 'toggled', 'toggle-id': 'list-mrq7j2' } - } - }, - { insert: 'ccc' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-2528578b-ecda-4f74-9fd7-8741d72dc8b3', - indent: 2, - list: { list: 'toggled', 'toggle-id': 'list-liu7dl' } - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-18bf68c3-9ef3-4874-929c-9b6bb1a00325' } - }, - { - insert: '\n', - attributes: { 'table-col': { width: '150' } } - }, - { insert: '\n', attributes: { 'table-col': { width: '150' } } }, - { - insert: '\n', - attributes: { 'table-col': { width: '150' } } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d44e74b4-b37f-48e0-b319-6327a6295a57', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-cpybie' - }, + colspan: '1', row: 'row-si1nah', cell: 'cell-cpybie', - rowspan: '1', - colspan: '1' - } - }, - { insert: 'aaa' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-3e545ee9-0c9a-42d7-a4d0-833edb8087f3', - list: { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-cpybie', - list: 'toggled', - 'toggle-id': 'list-kjl2ik' - }, + list: 'toggled', + 'toggle-id': 'list-kjl2ik' + }, + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + }) + .insert('bb') + .insert('\n', { + indent: 1, + 'block-id': 'block-5f1225ad-370f-46ab-8f1e-18b277b5095f', + list: { rowspan: '1', colspan: '1', row: 'row-si1nah', - cell: 'cell-cpybie' - } - }, - { insert: 'bb' }, - { - insert: '\n', - attributes: { - indent: 1, - 'block-id': 'block-5f1225ad-370f-46ab-8f1e-18b277b5095f', - list: { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-cpybie', - list: 'toggled', - 'toggle-id': 'list-eei1x5' - }, + cell: 'cell-cpybie', + list: 'toggled', + 'toggle-id': 'list-eei1x5' + }, + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + }) + .insert('ccc') + .insert('\n', { + indent: 2, + 'block-id': 'block-a77fdc11-ad24-431b-9ca2-09e32db94ac2', + list: { rowspan: '1', colspan: '1', row: 'row-si1nah', - cell: 'cell-cpybie' - } - }, - { insert: 'ccc' }, - { - insert: '\n', - attributes: { - indent: 2, - 'block-id': 'block-a77fdc11-ad24-431b-9ca2-09e32db94ac2', - list: { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-cpybie', - list: 'toggled', - 'toggle-id': 'list-30us3c' - }, + cell: 'cell-cpybie', + list: 'toggled', + 'toggle-id': 'list-30us3c' + }, + rowspan: '1', + colspan: '1', + row: 'row-si1nah', + cell: 'cell-cpybie' + }) + .insert('\n', { + 'block-id': 'block-d44e74b4-b37f-48e0-b319-6327a6295a57', + 'table-cell-line': { rowspan: '1', colspan: '1', row: 'row-si1nah', cell: 'cell-cpybie' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d44e74b4-b37f-48e0-b319-6327a6295a57', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-cpybie' - }, - row: 'row-si1nah', - cell: 'cell-cpybie', + }, + row: 'row-si1nah', + cell: 'cell-cpybie', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-2c274c8a-757d-4892-8db8-1a7999f7ab51', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-2c274c8a-757d-4892-8db8-1a7999f7ab51', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-al1z64' - }, + colspan: '1', row: 'row-si1nah', - cell: 'cell-al1z64', + cell: 'cell-al1z64' + }, + row: 'row-si1nah', + cell: 'cell-al1z64', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-85931afe-1879-471c-bb4b-89e7bd517fe9', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-85931afe-1879-471c-bb4b-89e7bd517fe9', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-si1nah', - cell: 'cell-q186pb' - }, + colspan: '1', row: 'row-si1nah', - cell: 'cell-q186pb', + cell: 'cell-q186pb' + }, + row: 'row-si1nah', + cell: 'cell-q186pb', + rowspan: '1', + colspan: '1' + }) + .insert('asdfasdfasdf') + .insert('\n', { + 'block-id': 'block-6e0522e8-c1eb-4c07-98df-2b07c533a139', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { insert: 'asdfasdfasdf' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-6e0522e8-c1eb-4c07-98df-2b07c533a139', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-7x2d1o', - cell: 'cell-6eid2t' - }, + colspan: '1', row: 'row-7x2d1o', - cell: 'cell-6eid2t', + cell: 'cell-6eid2t' + }, + row: 'row-7x2d1o', + cell: 'cell-6eid2t', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-4b3d0bd0-9175-45e9-955c-e8164f4b5376', + row: 'row-7x2d1o', + cell: 'cell-m1alad', + rowspan: '1', + colspan: '1', + list: { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-4b3d0bd0-9175-45e9-955c-e8164f4b5376', + colspan: '1', row: 'row-7x2d1o', cell: 'cell-m1alad', + list: 'ordered' + } + }) + .insert('asdfasdfasdf') + .insert('\n', { + 'block-id': 'block-08610089-cb05-4366-bb1e-a0787d5b11bf', + 'table-cell-line': { rowspan: '1', colspan: '1', - list: { - rowspan: '1', - colspan: '1', - row: 'row-7x2d1o', - cell: 'cell-m1alad', - list: 'ordered' - } - } - }, - { insert: 'asdfasdfasdf' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-08610089-cb05-4366-bb1e-a0787d5b11bf', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-7x2d1o', - cell: 'cell-dm1l2p' - }, row: 'row-7x2d1o', - cell: 'cell-dm1l2p', + cell: 'cell-dm1l2p' + }, + row: 'row-7x2d1o', + cell: 'cell-dm1l2p', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-c22b5125-8df3-432f-bd55-5ff456e41b4e', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-c22b5125-8df3-432f-bd55-5ff456e41b4e', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-o0ujua', - cell: 'cell-82g0ca' - }, + colspan: '1', row: 'row-o0ujua', - cell: 'cell-82g0ca', + cell: 'cell-82g0ca' + }, + row: 'row-o0ujua', + cell: 'cell-82g0ca', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-7c6320e4-acaf-4ab4-8355-c9b00408c9c1', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-7c6320e4-acaf-4ab4-8355-c9b00408c9c1', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-o0ujua', - cell: 'cell-wv6ozp' - }, + colspan: '1', row: 'row-o0ujua', - cell: 'cell-wv6ozp', + cell: 'cell-wv6ozp' + }, + row: 'row-o0ujua', + cell: 'cell-wv6ozp', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-d1bb7bed-e69e-4807-8d20-2d28fef8d08f', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-d1bb7bed-e69e-4807-8d20-2d28fef8d08f', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-o0ujua', - cell: 'cell-ldt53x' - }, + colspan: '1', row: 'row-o0ujua', - cell: 'cell-ldt53x', + cell: 'cell-ldt53x' + }, + row: 'row-o0ujua', + cell: 'cell-ldt53x', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { 'block-id': 'block-28f28cb8-51a2-4156-acf9-2380e1349745' }) + .insert([{ divider: true }]) + .insert('\n', { 'block-id': 'block-a1193252-c0c8-47fe-b9f6-32c8b01a1619' }) + .insert('\n', { 'table-col': { width: '150' } }) + .insert('\n\n', { 'table-col': { width: '150' } }) + .insert('/This is a test.') + .insert('\n', { + 'block-id': 'block-14188df0-a63f-4317-9a6d-91b96a7ac9fe', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-28f28cb8-51a2-4156-acf9-2380e1349745' } - }, - { insert: { divider: true } }, - { - insert: '\n', - attributes: { 'block-id': 'block-a1193252-c0c8-47fe-b9f6-32c8b01a1619' } - }, - { insert: '\n', attributes: { 'table-col': { width: '150' } } }, - { - insert: '\n\n', - attributes: { 'table-col': { width: '150' } } - }, - { insert: '/This is a test.' }, - { - insert: '\n', - attributes: { - 'block-id': 'block-14188df0-a63f-4317-9a6d-91b96a7ac9fe', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-5ixdvv', - cell: 'cell-9tgyed' - }, + colspan: '1', row: 'row-5ixdvv', - cell: 'cell-9tgyed', + cell: 'cell-9tgyed' + }, + row: 'row-5ixdvv', + cell: 'cell-9tgyed', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-7e5ba2af-9903-457d-adf4-2a79be81d823', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-7e5ba2af-9903-457d-adf4-2a79be81d823', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-5ixdvv', - cell: 'cell-xc56e9' - }, + colspan: '1', row: 'row-5ixdvv', - cell: 'cell-xc56e9', + cell: 'cell-xc56e9' + }, + row: 'row-5ixdvv', + cell: 'cell-xc56e9', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-eb6cad93-caf7-4848-8adf-415255139268', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-eb6cad93-caf7-4848-8adf-415255139268', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-5ixdvv', - cell: 'cell-xrze3u' - }, + colspan: '1', row: 'row-5ixdvv', - cell: 'cell-xrze3u', + cell: 'cell-xrze3u' + }, + row: 'row-5ixdvv', + cell: 'cell-xrze3u', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-5bb547a2-6f71-4624-80c7-d0e1318c81a2', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-5bb547a2-6f71-4624-80c7-d0e1318c81a2', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-xbzv98', - cell: 'cell-lie0ng' - }, + colspan: '1', row: 'row-xbzv98', - cell: 'cell-lie0ng', + cell: 'cell-lie0ng' + }, + row: 'row-xbzv98', + cell: 'cell-lie0ng', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-b506de0d-efb6-4bd7-ba8e-2186cc57903e', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-b506de0d-efb6-4bd7-ba8e-2186cc57903e', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-xbzv98', - cell: 'cell-s9sow1' - }, + colspan: '1', row: 'row-xbzv98', - cell: 'cell-s9sow1', + cell: 'cell-s9sow1' + }, + row: 'row-xbzv98', + cell: 'cell-s9sow1', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-42d2ad20-5521-40e3-a88d-fe6906176e61', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-42d2ad20-5521-40e3-a88d-fe6906176e61', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-xbzv98', - cell: 'cell-nodtcj' - }, + colspan: '1', row: 'row-xbzv98', - cell: 'cell-nodtcj', + cell: 'cell-nodtcj' + }, + row: 'row-xbzv98', + cell: 'cell-nodtcj', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-7d3e4216-3f68-4dd6-bc77-4a9fad4ba008', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-7d3e4216-3f68-4dd6-bc77-4a9fad4ba008', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-5bqfil', - cell: 'cell-c8c0f3' - }, + colspan: '1', row: 'row-5bqfil', - cell: 'cell-c8c0f3', + cell: 'cell-c8c0f3' + }, + row: 'row-5bqfil', + cell: 'cell-c8c0f3', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-6671f221-551e-47fb-9b7d-9043b6b12cdc', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-6671f221-551e-47fb-9b7d-9043b6b12cdc', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-5bqfil', - cell: 'cell-jvxxif' - }, + colspan: '1', row: 'row-5bqfil', - cell: 'cell-jvxxif', + cell: 'cell-jvxxif' + }, + row: 'row-5bqfil', + cell: 'cell-jvxxif', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { + 'block-id': 'block-51e3161b-0437-4fe3-ac4f-129a93a93fc3', + 'table-cell-line': { rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { - 'block-id': 'block-51e3161b-0437-4fe3-ac4f-129a93a93fc3', - 'table-cell-line': { - rowspan: '1', - colspan: '1', - row: 'row-5bqfil', - cell: 'cell-rmjpze' - }, + colspan: '1', row: 'row-5bqfil', - cell: 'cell-rmjpze', - rowspan: '1', - colspan: '1' - } - }, - { - insert: '\n', - attributes: { 'block-id': 'block-21099df0-afb2-4cd3-834d-bb37800eb06a' } - } - ] + cell: 'cell-rmjpze' + }, + row: 'row-5bqfil', + cell: 'cell-rmjpze', + rowspan: '1', + colspan: '1' + }) + .insert('\n', { 'block-id': 'block-21099df0-afb2-4cd3-834d-bb37800eb06a' }) const ydoc = new Y.Doc() const ytext = ydoc.getText('id') ytext.applyDelta(initialContent) - const changeEvent = [ - { retain: 90 }, - { delete: 4 }, - { - retain: 1, - attributes: { - layout: null, - 'layout-width': null, - 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' - } - } - ] + const changeEvent = delta.create().retain(90).delete(4).retain(1, { + layout: null, + 'layout-width': null, + 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' + }) ytext.applyDelta(changeEvent) - const delta = ytext.getContent() - t.compare(delta.ops[40].toJSON(), { + const d = ytext.getContent() + t.compare(list.toArray(d.children)[40].toJSON(), { insert: '\n', - attributes: { + format: { 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' } }) @@ -1552,12 +1241,12 @@ export const testDeltaAfterConcurrentFormatting = tc => { */ const deltas = [] text1.observe(event => { - if (event.delta.ops.length > 0) { + if (event.delta.children.len > 0) { deltas.push(event.delta.toJSON()) } }) testConnector.flushAllMessages() - t.compare(deltas, [[{ retain: 2, attributes: { bold: true } }, { retain: 1 }, { retain: 1, attributes: { bold: null } }]]) + t.compare(deltas, [[{ retain: 2, format: { bold: true } }, { retain: 1 }, { retain: 1, format: { bold: null } }]]) } /** @@ -1576,21 +1265,21 @@ export const testBasicInsertAndDelete = tc => { text0.insert(0, 'abc') t.assert(text0.toString() === 'abc', 'Basic insert works') - t.compare(eventDelta, delta.createTextDelta().insert('abc')) + t.compare(eventDelta, delta.create().insert('abc')) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete works (position 0)') - t.compare(eventDelta, delta.createTextDelta().delete(1)) + t.compare(eventDelta, delta.create().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1)) + t.compare(eventDelta, delta.create().retain(1).delete(1)) users[0].transact(() => { text0.insert(0, '1') text0.delete(0, 1) }) - t.compare(eventDelta, delta.createTextDelta()) + t.compare(eventDelta, delta.create()) compare(users) } @@ -1606,30 +1295,30 @@ export const testBasicFormat = tc => { }) text0.insert(0, 'abc', { bold: true }) t.assert(text0.toString() === 'abc', 'Basic insert with attributes works') - t.compare(text0.getContent(), delta.createTextDelta().insert('abc', { bold: true }).done()) - t.compare(eventDelta, delta.createTextDelta().insert('abc', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('abc', { bold: true })) + t.compare(eventDelta, delta.create().insert('abc', { bold: true })) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)') - t.compare(text0.getContent(), delta.createTextDelta().insert('bc', { bold: true })) - t.compare(eventDelta, delta.createTextDelta().delete(1)) + t.compare(text0.getContent(), delta.create().insert('bc', { bold: true })) + t.compare(eventDelta, delta.create().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(text0.getContent(), delta.createTextDelta().insert('b', { bold: true })) - t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1)) + t.compare(text0.getContent(), delta.create().insert('b', { bold: true })) + t.compare(eventDelta, delta.create().retain(1).delete(1)) text0.insert(0, 'z', { bold: true }) t.assert(text0.toString() === 'zb') - t.compare(text0.getContent(), delta.createTextDelta().insert('zb', { bold: true })) - t.compare(eventDelta, delta.createTextDelta().insert('z', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('zb', { bold: true })) + t.compare(eventDelta, delta.create().insert('z', { bold: true })) // @ts-ignore t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker') text0.insert(0, 'y') t.assert(text0.toString() === 'yzb') - t.compare(text0.getContent(), delta.createTextDelta().insert('y').insert('zb', { bold: true })) - t.compare(eventDelta, delta.createTextDelta().insert('y')) + t.compare(text0.getContent(), delta.create().insert('y').insert('zb', { bold: true })) + t.compare(eventDelta, delta.create().insert('y')) text0.format(0, 2, { bold: null }) t.assert(text0.toString() === 'yzb') - t.compare(text0.getContent(), delta.createTextDelta().insert('yz').insert('b', { bold: true })) - t.compare(eventDelta, delta.createTextDelta().retain(1).retain(1, { bold: null })) + t.compare(text0.getContent(), delta.create().insert('yz').insert('b', { bold: true })) + t.compare(eventDelta, delta.create().retain(1).retain(1, { bold: null })) compare(users) } @@ -1638,19 +1327,19 @@ export const testBasicFormat = tc => { */ export const testFalsyFormats = tc => { const { users, text0 } = init(tc, { users: 2 }) - let delta + let delta = text0.getContent().toJSON().children text0.observe(event => { - delta = event.delta.toJSON() + delta = event.delta.toJSON().children }) text0.insert(0, 'abcde', { falsy: false }) - t.compare(text0.getContent().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }]) - t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON().children, [{ insert: 'abcde', format: { falsy: false } }]) + t.compare(delta, [{ insert: 'abcde', format: { falsy: false } }]) text0.format(1, 3, { falsy: true }) - t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) - t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }]) + t.compare(text0.getContent().toJSON().children, [{ insert: 'a', format: { falsy: false } }, { insert: 'bcd', format: { falsy: true } }, { insert: 'e', format: { falsy: false } }]) + t.compare(delta, [{ retain: 1 }, { retain: 3, format: { falsy: true } }]) text0.format(2, 1, { falsy: false }) - t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }]) - t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }]) + t.compare(text0.getContent().toJSON().children, [{ insert: 'a', format: { falsy: false } }, { insert: 'b', format: { falsy: true } }, { insert: 'c', format: { falsy: false } }, { insert: 'd', format: { falsy: true } }, { insert: 'e', format: { falsy: false } }]) + t.compare(delta, [{ retain: 2 }, { retain: 1, format: { falsy: false } }]) compare(users) } @@ -1661,19 +1350,19 @@ export const testMultilineFormat = _tc => { const ydoc = new Y.Doc() const testText = ydoc.getText('test') testText.insert(0, 'Test\nMulti-line\nFormatting') - testText.applyDelta([ - { retain: 4, attributes: { bold: true } }, - { retain: 1 }, // newline character - { retain: 10, attributes: { bold: true } }, - { retain: 1 }, // newline character - { retain: 10, attributes: { bold: true } } - ]) - t.compare(testText.getContent().toJSON(), [ - { insert: 'Test', attributes: { bold: true } }, + const tt = delta.create() + .retain(4, { bold: true }) + .retain(1) // newline character + .retain(10, { bold: true }) + .retain(1) // newline character + .retain(10, { bold: true }) + testText.applyDelta(tt) + t.compare(testText.getContent().toJSON().children, [ + { insert: 'Test', format: { bold: true } }, { insert: '\n' }, - { insert: 'Multi-line', attributes: { bold: true } }, + { insert: 'Multi-line', format: { bold: true } }, { insert: '\n' }, - { insert: 'Formatting', attributes: { bold: true } } + { insert: 'Formatting', format: { bold: true } } ]) } @@ -1683,17 +1372,17 @@ export const testMultilineFormat = _tc => { export const testNotMergeEmptyLinesFormat = _tc => { const ydoc = new Y.Doc() const testText = ydoc.getText('test') - testText.applyDelta([ - { insert: 'Text' }, - { insert: '\n', attributes: { title: true } }, - { insert: '\nText' }, - { insert: '\n', attributes: { title: true } } - ]) - t.compare(testText.getContent().toJSON(), [ + testText.applyDelta(delta.create() + .insert('Text') + .insert('\n', { title: true }) + .insert('\nText') + .insert('\n', { title: true }) + ) + t.compare(testText.getContent().toJSON().children, [ { insert: 'Text' }, - { insert: '\n', attributes: { title: true } }, + { insert: '\n', format: { title: true } }, { insert: '\nText' }, - { insert: '\n', attributes: { title: true } } + { insert: '\n', format: { title: true } } ]) } @@ -1703,19 +1392,19 @@ export const testNotMergeEmptyLinesFormat = _tc => { export const testPreserveAttributesThroughDelete = _tc => { const ydoc = new Y.Doc() const testText = ydoc.getText('test') - testText.applyDelta([ - { insert: 'Text' }, - { insert: '\n', attributes: { title: true } }, - { insert: '\n' } - ]) - testText.applyDelta([ - { retain: 4 }, - { delete: 1 }, - { retain: 1, attributes: { title: true } } - ]) - t.compare(testText.getContent().toJSON(), [ + testText.applyDelta(delta.create() + .insert('Text') + .insert('\n', { title: true }) + .insert('\n') + ) + testText.applyDelta(delta.create() + .retain(4) + .delete(1) + .retain(1, { title: true }) + ) + t.compare(testText.getContent().toJSON().children, [ { insert: 'Text' }, - { insert: '\n', attributes: { title: true } } + { insert: '\n', format: { title: true } } ]) } @@ -1724,11 +1413,9 @@ export const testPreserveAttributesThroughDelete = _tc => { */ export const testGetDeltaWithEmbeds = tc => { const { text0 } = init(tc, { users: 1 }) - text0.applyDelta([{ - insert: { linebreak: 's' } - }]) - t.compare(text0.getContent().toJSON(), [{ - insert: { linebreak: 's' } + text0.applyDelta(delta.create().insert([{ linebreak: 's' }])) + t.compare(text0.getContent().toJSON().children, [{ + insert: [{ linebreak: 's' }] }]) } @@ -1737,21 +1424,21 @@ export const testGetDeltaWithEmbeds = tc => { */ export const testTypesAsEmbed = tc => { const { text0, text1, testConnector } = init(tc, { users: 2 }) - text0.applyDelta([{ - insert: new Y.Map([['key', 'val']]) - }]) - t.compare(/** @type {delta.InsertEmbedOp} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' }) + text0.applyDelta(delta.create() + .insert([new Y.Map([['key', 'val']])]) + ) + t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children[0]?.insert, { key: 'val' }) let firedEvent = false text1.observe(event => { const d = event.delta - t.assert(d.ops.length === 1) - t.compare(d.ops.map(x => /** @type {any} */ (x).insert.toJSON()), [{ key: 'val' }]) + t.assert(d.children.len === 1) + t.compare(list.toArray(d.children).map(x => /** @type {any} */ (x).insert.toJSON()), [{ key: 'val' }]) firedEvent = true }) testConnector.flushAllMessages() - const delta = text1.getContent().toJSON() - t.assert(delta.length === 1) - t.compare(/** @type {any} */ (delta[0]).insert.toJSON(), { key: 'val' }) + const dd = text1.getContent().toJSON().children + t.assert(dd?.length === 1) + t.compare(/** @type {any} */ (dd?.[0]).insert.toJSON(), { key: 'val' }) t.assert(firedEvent, 'fired the event observer containing a Type-Embed') } @@ -1762,32 +1449,24 @@ export const testSnapshot = tc => { const { text0 } = init(tc, { users: 1 }) const doc0 = /** @type {Y.Doc} */ (text0.doc) doc0.gc = false - text0.applyDelta([{ - insert: 'abcd' - }]) + text0.applyDelta(delta.create().insert('abcd')) const snapshot1 = Y.snapshot(doc0) - text0.applyDelta([{ - retain: 1 - }, { - insert: 'x' - }, { - delete: 1 - }]) + text0.applyDelta(delta.create() + .retain(1) + .insert('x') + .delete(1)) const snapshot2 = Y.snapshot(doc0) - text0.applyDelta([{ - retain: 2 - }, { - delete: 3 - }, { - insert: 'x' - }, { - delete: 1 - }]) + text0.applyDelta(delta.create() + .retain(2) + .delete(3) + .insert('x') + .delete(1) + ) const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) - t.compare(state1.toJSON(), [{ insert: 'abcd' }]) + t.compare(state1.toJSON().children, [{ insert: 'abcd' }]) const state2 = text0.getContent(createAttributionManagerFromSnapshots(snapshot2)) - t.compare(state2.toJSON(), [{ insert: 'axcd' }]) - const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON() + t.compare(state2.toJSON().children, [{ insert: 'axcd' }]) + const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON().children const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }] t.compare(state2Diff, expected) } @@ -1799,17 +1478,16 @@ export const testSnapshotDeleteAfter = tc => { const { text0 } = init(tc, { users: 1 }) const doc0 = /** @type {Y.Doc} */ (text0.doc) doc0.gc = false - text0.applyDelta([{ - insert: 'abcd' - }]) + text0.applyDelta(delta.create() + .insert('abcd') + ) const snapshot1 = Y.snapshot(doc0) - text0.applyDelta([{ - retain: 4 - }, { - insert: 'e' - }]) + text0.applyDelta(delta.create() + .retain(4) + .insert('e') + ) const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) - t.compare(state1, delta.createTextDelta().insert('abcd')) + t.compare(state1, delta.create().insert('abcd')) } /** @@ -1828,8 +1506,8 @@ export const testToDeltaEmbedAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 }) - const delta0 = text0.getContent().toJSON() - t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' }, attributes: { width: 100 } }, { insert: 'b', attributes: { bold: true } }]) + const delta0 = text0.getContent().toJSON().children + t.compare(delta0, [{ insert: 'a', format: { bold: true } }, { insert: [{ image: 'imageSrc.png' }], format: { width: 100 } }, { insert: 'b', format: { bold: true } }]) } /** @@ -1839,8 +1517,8 @@ export const testToDeltaEmbedNoAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }) - const delta0 = text0.getContent().toJSON() - t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' } }, { insert: 'b', attributes: { bold: true } }], 'toDelta does not set attributes key when no attributes are present') + const delta0 = text0.getContent().toJSON().children + t.compare(delta0, [{ insert: 'a', format: { bold: true } }, { insert: [{ image: 'imageSrc.png' }] }, { insert: 'b', format: { bold: true } }], 'toDelta does not set attributes key when no attributes are present') } /** @@ -1880,7 +1558,7 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { }) testConnector.flushAllMessages() /** - * @type {Array>} + * @type {Array>} */ const deltas = [] text0.observe(event => { @@ -1891,10 +1569,10 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { }) text1.format(0, 1, { LIST_STYLES: 'number' }) testConnector.flushAllMessages() - const filteredDeltas = deltas.filter(d => d.ops.length > 0) + const filteredDeltas = deltas.filter(d => d.children.len > 0) t.assert(filteredDeltas.length === 2) - t.compare(filteredDeltas[0].toJSON(), [ - { retain: 1, attributes: { LIST_STYLES: 'number' } } + t.compare(filteredDeltas[0].toJSON().children, [ + { retain: 1, format: { LIST_STYLES: 'number' } } ]) t.compare(filteredDeltas[0], filteredDeltas[1]) } @@ -2143,11 +1821,11 @@ export const testFormattingBug = async _tc => { Y.applyUpdate(ydoc2, Y.encodeStateAsUpdate(ydoc1)) const text2 = ydoc2.getText() const expectedResult = [ - { insert: '\n', attributes: { url: 'http://example.com' } }, - { insert: '\n', attributes: { url: 'http://docs.yjs.dev' } }, - { insert: '\n', attributes: { url: 'http://example.com' } } + { insert: '\n', format: { url: 'http://example.com' } }, + { insert: '\n', format: { url: 'http://docs.yjs.dev' } }, + { insert: '\n', format: { url: 'http://example.com' } } ] - t.compare(text1.getContent().toJSON(), expectedResult) + t.compare(text1.getContent().toJSON().children, expectedResult) t.compare(text1.getContent().toJSON(), text2.getContent().toJSON()) console.log(text1.getContent().toJSON()) } @@ -2174,11 +1852,11 @@ export const testDeleteFormatting = _tc => { const expected = [ { insert: 'Attack ships ' }, - { insert: 'on ', attributes: { bold: true } }, + { insert: 'on ', format: { bold: true } }, { insert: 'fire off the shoulder of Orion.' } ] - t.compare(text.getContent().toJSON(), expected) - t.compare(text2.getContent().toJSON(), expected) + t.compare(text.getContent().toJSON().children, expected) + t.compare(text2.getContent().toJSON().children, expected) } /** @@ -2195,15 +1873,15 @@ export const testAttributedContent = _tc => { attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, []), createIdMapFromIdSet(tr.deleteSet, [])) }) t.group('insert / delete / format', () => { - ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) - const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') + ytext.applyDelta(delta.create().retain(4, { italic: true }).retain(2).delete(5).insert('attributions')) + const expectedContent = delta.create().insert('Hell', { italic: true }, { format: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!') const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) }) t.group('unformat', () => { - ytext.applyDelta([{ retain: 5, attributes: { italic: null } }]) - const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!') + ytext.applyDelta(delta.create().retain(5, { italic: null })) + const expectedContent = delta.create().insert('Hell', null, { format: { italic: [] } }).insert('o attributions!') const attributedContent = ytext.getContent(attributionManager) console.log(attributedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) @@ -2221,7 +1899,7 @@ export const testAttributedDiffing = _tc => { ydoc.clientID = 1 Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocVersion0)) const ytext = ydoc.getText() - ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }]) + ytext.applyDelta(delta.create().retain(4, { italic: true }).retain(2).delete(5).insert('attributions')) // this represents to all insertions of ydoc const insertionSet = Y.createInsertionSetFromStructStore(ydoc.store, false) const deleteSet = Y.createDeleteSetFromStructStore(ydoc.store) @@ -2237,7 +1915,7 @@ export const testAttributedDiffing = _tc => { // we render the attributed content with the attributionManager const attributedContent = ytext.getContent(attributionManager) console.log(JSON.stringify(attributedContent.toJSON(), null, 2)) - const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') + const expectedContent = delta.create().insert('Hell', { italic: true }, { format: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!') t.assert(attributedContent.equals(expectedContent)) console.log(Y.encodeIdMap(attributedInsertions).length) } @@ -2414,12 +2092,9 @@ const qChanges = [ const ytext = y.getText('text') const insertPos = prng.int32(gen, 0, ytext.toString().length) const text = charCounter++ + prng.word(gen) - const ops = [] - if (insertPos > 0) { - ops.push({ retain: insertPos }) - } - ops.push({ insert: text }, { insert: '\n', format: { 'code-block': true } }) - ytext.applyDelta(ops) + const d = delta.create() + d.retain(insertPos).insert(text).insert('\n', { 'code-block': true }) + ytext.applyDelta(d) }, /** * @param {Y.Doc} y @@ -2429,32 +2104,29 @@ const qChanges = [ const ytext = y.getText('text') const contentLen = ytext.toString().length let currentPos = math.max(0, prng.int32(gen, 0, contentLen - 1)) - /** - * @type {Array} - */ - const ops = currentPos > 0 ? [{ retain: currentPos }] : [] + const d = delta.create().retain(currentPos) // create max 3 ops for (let i = 0; i < 7 && currentPos < contentLen; i++) { prng.oneOf(gen, [ () => { // format const retain = math.min(prng.int32(gen, 0, contentLen - currentPos), 5) const format = prng.oneOf(gen, marks) - ops.push({ retain, attributes: format }) + d.retain(retain, format) currentPos += retain }, () => { // insert const attrs = prng.oneOf(gen, marksChoices) const text = prng.word(gen, 1, 3) - ops.push({ insert: text, attributes: attrs }) + d.insert(text, attrs) }, () => { // delete const delLen = math.min(prng.int32(gen, 0, contentLen - currentPos), 10) - ops.push({ delete: delLen }) + d.delete(delLen) currentPos += delLen } ])() } - ytext.applyDelta(ops) + ytext.applyDelta(d) } ] @@ -2463,20 +2135,9 @@ const qChanges = [ */ const checkResult = result => { for (let i = 1; i < result.testObjects.length; i++) { - /** - * @param {any} d - */ - const typeToObject = d => d.insert instanceof Y.AbstractType ? d.insert.toJSON() : d - t.info('length of text = ' + result.users[i - 1].getText('text').length) - t.measureTime('original toDelta perf', () => { - result.users[i - 1].getText('text').getContent().toJSON().map(typeToObject) - }) - t.measureTime('getContent(attributionManager) performance)', () => { - result.users[i - 1].getText('text').getContent() - }) - const p1 = result.users[i - 1].getText('text').getContent().toJSON().map(typeToObject) - const p2 = result.users[i].getText('text').getContent().toJSON().map(typeToObject) + const p1 = result.users[i - 1].getText('text').getContentDeep().children + const p2 = result.users[i].getText('text').getContentDeep().children t.compare(p1, p2) } // Uncomment this to find formatting-cleanup issues diff --git a/tsconfig.json b/tsconfig.json index 71beb314c..e69f76521 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,5 +20,5 @@ } }, "include": ["./src/**/*.js", "./tests/**/*.js"], - "exclude": ["../lib0/**"] + "exclude": ["./node_modules/**/*"] } From 1ce8154d648ec74e0e93a7028db8cd7927f29516 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 22 Oct 2025 23:25:30 +0200 Subject: [PATCH 351/362] fixed several v2 issues --- src/types/AbstractType.js | 115 ++++++++++++++++++++++++++++---------- src/types/YText.js | 35 +----------- src/types/YXmlFragment.js | 1 + src/utils/YEvent.js | 86 ++++++++-------------------- 4 files changed, 112 insertions(+), 125 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 36c9b4a08..428055df2 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -16,7 +16,12 @@ import { ContentEmbed, getItemCleanStart, noAttributionsManager, - ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, // eslint-disable-line + transact, + ItemTextListPosition, + insertText, + deleteText, + ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, + YXmlElement, // eslint-disable-line } from '../internals.js' import * as delta from 'lib0/delta' @@ -178,7 +183,7 @@ export const findMarker = (yarray, index) => { // window.lengths.push(marker.index - pindex) // console.log('distance', marker.index - pindex, 'len', p && p.parent.length) // } - if (marker !== null && math.abs(marker.index - pindex) < /** @type {YText|YArray} */ (p.parent).length / maxSearchMarker) { + if (marker !== null && math.abs(marker.index - pindex) < /** @type {any} */ (p.parent).length / maxSearchMarker) { // adjust existing marker overwriteMarker(marker, p, pindex) return marker @@ -307,6 +312,10 @@ export class AbstractType { * @type {null | Array} */ this._searchMarker = null + /** + * @type {EventDelta?} + */ + this._prelim = null } /** @@ -337,6 +346,10 @@ export class AbstractType { _integrate (y, item) { this.doc = y this._item = item + if (this._prelim) { + this.applyDelta(this._prelim) + this._prelim = null + } } /** @@ -437,6 +450,8 @@ export class AbstractType { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * + * @template {boolean} [Deep=false] + * * @param {AbstractAttributionManager} am * @param {Object} [opts] * @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender] @@ -445,16 +460,19 @@ export class AbstractType { * @param {Set?} [opts.renderAttrs] - set of attrs to render. if null, render all attributes * @param {boolean} [opts.renderChildren] - if true, retain rendered+attributed deletes only * @param {import('../utils/IdSet.js').IdSet?} [opts.deletedItems] - used for computing prevItem in attributes - * @return {EventDelta} The Delta representation of this type. + * @param {Set|Map|null} [opts.modified] - set of types that should be rendered as modified children + * @param {Deep} [opts.deep] - render child types as delta + * @return {Deep extends true ? ToDeepEventDelta : EventDelta} The Delta representation of this type. * * @public */ - getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null } = {}) { + getContent (am = noAttributionsManager, opts = {}) { + const { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null, modified = null, deep = false } = opts /** * @type {EventDelta} */ - const d = /** @type {any} */ (delta.create()) - typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deletedItems, itemsToRender) + const d = /** @type {any} */ (delta.create(this.nodeName || null)) + typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deep, modified, deletedItems, itemsToRender) if (renderChildren) { /** * @type {delta.FormattingAttributes} @@ -545,15 +563,21 @@ export class AbstractType { usingCurrentAttributes = true if (c.deleted ? retainDeletes : retainInserts) { d.retain(c.content.getLength(), null, attribution ?? {}) + } else if (deep && c.content.constructor === ContentType) { + d.insert([/** @type {any} */ (c.content).type.getContent(am, opts)], null, attribution) } else { d.insert(c.content.getContent(), null, attribution) } } else if (renderDelete) { d.delete(1) } else if (retainContent) { - d.usedAttributes = changedAttributes - usingChangedAttributes = true - d.retain(1) + if (c.content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (c.content).type)) { + d.modify(/** @type {any} */ (c.content).type.getContent(am, opts)) + } else { + d.usedAttributes = changedAttributes + usingChangedAttributes = true + d.retain(1) + } } break case ContentFormat: { @@ -663,23 +687,52 @@ export class AbstractType { * @return {ToDeepEventDelta} */ getContentDeep (am = noAttributionsManager) { - const d = this.getContent(am) - d.children.forEach(op => { - if (op instanceof delta.InsertOp) { - op.insert = /** @type {any} */ (op.insert.map(ins => - ins instanceof AbstractType - // @ts-ignore - ? ins.getContentDeep(am) - : ins) - ) - } - }) - d.attrs.forEach((op) => { - if (delta.$insertOp.check(op) && op.value instanceof AbstractType) { - op.value = op.value.getContentDeep(am) - } - }) - return /** @type {any} */ (d.done()) + return /** @type {any} */ (this.getContent(am, { deep: true })) + } + + /** + * Apply a {@link Delta} on this shared type. + * + * @param {delta.Delta} d The changes to apply on this element. + * @param {AbstractAttributionManager} am + * + * @public + */ + applyDelta (d, am = noAttributionsManager) { + if (this.doc == null) + (this._prelim || (this._prelim = /** @type {any} */ (delta.create()))).apply(d) + else { + // @todo this was moved here from ytext. Make this more generic + transact(this.doc, transaction => { + const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) + for (const op of d.children) { + if (delta.$textOp.check(op)) { + insertText(transaction, this, currPos, op.insert, op.format || {}) + } else if (delta.$insertOp.check(op)) { + for (let i = 0; i < op.insert.length; i++) { + let ins = op.insert[i] + if (delta.$deltaAny.check(ins)) { + if (ins.name != null) { + const t = new YXmlElement(ins.name) + t.applyDelta(ins) + ins = t + } else { + error.unexpectedCase() + } + } + insertText(transaction, this, currPos, ins, op.format || {}) + } + } else if (delta.$retainOp.check(op)) { + currPos.formatText(transaction, this, op.retain, op.format || {}) + } else if (delta.$deleteOp.check(op)) { + deleteText(transaction, currPos, op.delete) + } else if (delta.$modifyOp.check(op)) { + /** @type {ContentType} */ (currPos.right?.content).type.applyDelta(op.modify) + currPos.formatText(transaction, this, 1, op.format || {}) + } + } + }) + } } } @@ -1230,13 +1283,16 @@ export const typeMapGetAll = (parent) => { * @param {YType_} parent * @param {Set?} attrsToRender * @param {import('../internals.js').AbstractAttributionManager} am + * @param {boolean} deep + * @param {Set|Map|null} [modified] - set of types that should be rendered as modified children * @param {import('../utils/IdSet.js').IdSet?} [deletedItems] * @param {import('../utils/IdSet.js').IdSet?} [itemsToRender] * * @private * @function */ -export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, itemsToRender) => { +export const typeMapGetDelta = (d, parent, attrsToRender, am, deep, modified, deletedItems, itemsToRender) => { + // @todo support modified ops! /** * @param {Item} item * @param {string} key @@ -1248,8 +1304,8 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, item const cs = [] am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1) const { deleted, attrs, content } = cs[cs.length - 1] - const c = array.last(content.getContent()) const attribution = createAttributionFromAttributionItems(attrs, deleted) + let c = array.last(content.getContent()) if (deleted) { if (itemsToRender == null || itemsToRender.hasId(item.lastId)) { d.unset(key, attribution, c) @@ -1262,6 +1318,9 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, item // nop } const prevValue = (prevContentItem !== item && itemsToRender?.hasId(prevContentItem.lastId)) ? array.last(prevContentItem.content.getContent()) : undefined + if (deep && c instanceof AbstractType) { + c = c.getContent(am) + } d.set(key, c, attribution, prevValue) } } diff --git a/src/types/YText.js b/src/types/YText.js index bb7fcbf30..8c035e603 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -337,7 +337,7 @@ const insertAttributes = (transaction, parent, currPos, attributes) => { * @private * @function **/ -const insertText = (transaction, parent, currPos, text, attributes) => { +export const insertText = (transaction, parent, currPos, text, attributes) => { currPos.currentAttributes.forEach((_val, key) => { if (attributes[key] === undefined) { attributes[key] = null @@ -546,7 +546,7 @@ export const cleanupYTextAfterTransaction = transaction => { * @private * @function */ -const deleteText = (transaction, currPos, length) => { +export const deleteText = (transaction, currPos, length) => { const startLength = length const startAttrs = map.copy(currPos.currentAttributes) const start = currPos.right @@ -745,37 +745,6 @@ export class YText extends AbstractType { return this.toString() } - /** - * Apply a {@link Delta} on this shared YText type. - * - * @param {delta.TextDelta} d The changes to apply on this element. - * @param {AbstractAttributionManager} am - * - * @public - */ - applyDelta (d, am = noAttributionsManager) { - if (this.doc !== null) { - transact(this.doc, transaction => { - const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) - for (const op of d.children) { - if (delta.$textOp.check(op)) { - insertText(transaction, this, currPos, op.insert, op.format || {}) - } else if (delta.$insertOp.check(op)) { - for (let i = 0; i < op.insert.length; i++) { - insertText(transaction, this, currPos, op.insert[i], op.format || {}) - } - } else if (delta.$retainOp.check(op)) { - currPos.formatText(transaction, this, op.retain, op.format || {}) - } else if (delta.$deleteOp.check(op)) { - deleteText(transaction, currPos, op.delete) - } - } - }) - } else { - /** @type {Array} */ (this._pending).push(() => this.applyDelta(d)) - } - } - /** * Insert text at a given index. * diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 50677afbb..14d5586bc 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -59,6 +59,7 @@ export class YXmlFragment extends AbstractType { constructor () { super() /** + * @todo remove _prelimContent * @type {Array|null} */ this._prelimContent = [] diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 181d4cfdb..ca25fabaa 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -41,14 +41,14 @@ export class YEvent { * @type {Transaction} */ this.transaction = transaction - /** - * @type {null | Map} - */ - this._keys = null /** * @type {(Target extends AbstractType ? D : delta.Delta)|null} */ this._delta = null + /** + * @type {(Target extends AbstractType ? import('../internals.js').ToDeepEventDelta : delta.Delta)|null} + */ + this._deltaDeep = null /** * @type {Array|null} */ @@ -102,63 +102,6 @@ export class YEvent { return this.transaction.deleteSet.hasId(struct.id) } - /** - * @type {Map} - */ - get keys () { - if (this._keys === null) { - if (this.transaction.doc._transactionCleanups.length === 0) { - throw error.create(errorComputeChanges) - } - const keys = new Map() - const target = this.target - // @ts-ignore - const changed = /** @type Set */ (this.transaction.changed.get(target)) - changed.forEach(key => { - if (key !== null) { - const item = /** @type {Item} */ (target._map.get(key)) - /** - * @type {'delete' | 'add' | 'update'} - */ - let action - let oldValue - if (this.adds(item)) { - let prev = item.left - while (prev !== null && this.adds(prev)) { - prev = prev.left - } - if (this.deletes(item)) { - if (prev !== null && this.deletes(prev)) { - action = 'delete' - oldValue = array.last(prev.content.getContent()) - } else { - return - } - } else { - if (prev !== null && this.deletes(prev)) { - action = 'update' - oldValue = array.last(prev.content.getContent()) - } else { - action = 'add' - oldValue = undefined - } - } - } else { - if (this.deletes(item)) { - action = 'delete' - oldValue = array.last(/** @type {Item} */ item.content.getContent()) - } else { - return // nop - } - } - keys.set(key, { action, oldValue }) - } - }) - this._keys = keys - } - return this._keys - } - /** * Check if a struct is added by this event. * @@ -172,14 +115,18 @@ export class YEvent { } /** + * @template {boolean} [Deep=false] * @param {AbstractAttributionManager} am - * @return {Target extends AbstractType ? D : delta.Delta} The Delta representation of this type. + * @param {object} [opts] + * @param {Deep} [opts.deep] + * @return {Target extends AbstractType ? (Deep extends true ? import('../internals.js').ToDeepEventDelta : D) : delta.Delta} The Delta representation of this type. * * @public */ - getDelta (am = noAttributionsManager) { + getDelta (am = noAttributionsManager, { deep } = {}) { const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)]) - return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged, deletedItems: this.transaction.deleteSet })) + const modified = deep ? this.transaction.changedParentTypes : null + return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: deep || this.childListChanged, deletedItems: this.transaction.deleteSet, deep: !!deep, modified })) } /** @@ -192,6 +139,17 @@ export class YEvent { get delta () { return /** @type {any} */ (this._delta ?? (this._delta = this.getDelta())) } + + /** + * Compute the changes in the delta format. + * A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document. + * + * @type {Target extends AbstractType ? D : delta.Delta} The Delta representation of this type. + * @public + */ + get deltaDeep () { + return /** @type {any} */ (this._deltaDeep ?? (this._deltaDeep = this.getDelta(noAttributionsManager, { deep: true }))) + } } /** From 1b07864d95cb4f891e8d77ea01b13edc6f54fc0c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 23 Oct 2025 13:32:37 +0200 Subject: [PATCH 352/362] bump to lib0 beta release --- package-lock.json | 33 +++++++++++++++++++++++++++------ package.json | 5 ++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0d6308d90..f7da2ba15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "14.0.0-8", "license": "MIT", "dependencies": { - "lib0": "^0.2.114" + "lib0": "^0.2.115-0" }, "devDependencies": { "@types/node": "^22.14.1", @@ -20,8 +20,7 @@ "rollup": "^4.37.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^5.9.3", - "yjs": "." + "typescript": "^5.9.3" }, "engines": { "node": ">=16.0.0", @@ -290,6 +289,28 @@ "yjs": "^14.0.0-1 || ^14 || ^13" } }, + "node_modules/@y/protocols/node_modules/lib0": { + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, "node_modules/acorn": { "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", @@ -3108,9 +3129,9 @@ } }, "node_modules/lib0": { - "version": "0.2.114", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", - "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "version": "0.2.115-0", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.115-0.tgz", + "integrity": "sha512-HAO2E6TJYi3c4DM9x0Ii24+ctnwTYMl/ZOacWB2XvGM2IljHRLzqynl9Voov3TpaO7c99ZQ79O8G7VX6PKP5IQ==", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" diff --git a/package.json b/package.json index c40d18afd..1ed36cb4b 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.114" + "lib0": "^0.2.115-0" }, "devDependencies": { "@types/node": "^22.14.1", @@ -96,8 +96,7 @@ "rollup": "^4.37.0", "standard": "^16.0.4", "tui-jsdoc-template": "^1.2.2", - "typescript": "^5.9.3", - "yjs": "." + "typescript": "^5.9.3" }, "engines": { "npm": ">=8.0.0", From 04359198d60a7a99ea3de65ba2fe49ea3c0455ae Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 23 Oct 2025 13:34:38 +0200 Subject: [PATCH 353/362] no forced tests for release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ed36cb4b..edc3a5a5a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "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 && 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", + "preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && 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", From 57bc4d3f42d404ab4d47c9f2cbb0eb6259949a83 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 23 Oct 2025 13:53:49 +0200 Subject: [PATCH 354/362] lint --- package.json | 4 ++-- src/types/AbstractType.js | 21 ++++++++++----------- src/types/YArray.js | 2 ++ src/types/YText.js | 3 +-- src/utils/YEvent.js | 4 ---- tests/updates.tests.js | 1 - 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index edc3a5a5a..554ea7f93 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,12 @@ "clean": "rm -rf dist docs", "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", + "dist": "npm run clean && rollup -c && tsc --skipLibCheck", "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 && 0serve ./docs/", - "preversion": "npm run lint && PRODUCTION=1 npm run dist && npm run docs && test -e dist/src/index.d.ts && test -e dist/yjs.cjs && test -e dist/yjs.cjs", + "preversion": "PRODUCTION=1 npm run dist && npm run docs && 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", diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 428055df2..461c47924 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -20,8 +20,7 @@ import { ItemTextListPosition, insertText, deleteText, - ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, - YXmlElement, // eslint-disable-line + ContentDoc, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, YXmlElement, // eslint-disable-line } from '../internals.js' import * as delta from 'lib0/delta' @@ -471,7 +470,7 @@ export class AbstractType { /** * @type {EventDelta} */ - const d = /** @type {any} */ (delta.create(this.nodeName || null)) + const d = /** @type {any} */ (delta.create(/** @type {any} */ (this).nodeName || null)) typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deep, modified, deletedItems, itemsToRender) if (renderChildren) { /** @@ -564,7 +563,7 @@ export class AbstractType { if (c.deleted ? retainDeletes : retainInserts) { d.retain(c.content.getLength(), null, attribution ?? {}) } else if (deep && c.content.constructor === ContentType) { - d.insert([/** @type {any} */ (c.content).type.getContent(am, opts)], null, attribution) + d.insert([/** @type {any} */(c.content).type.getContent(am, opts)], null, attribution) } else { d.insert(c.content.getContent(), null, attribution) } @@ -699,15 +698,15 @@ export class AbstractType { * @public */ applyDelta (d, am = noAttributionsManager) { - if (this.doc == null) + if (this.doc == null) { (this._prelim || (this._prelim = /** @type {any} */ (delta.create()))).apply(d) - else { + } else { // @todo this was moved here from ytext. Make this more generic transact(this.doc, transaction => { const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am) for (const op of d.children) { if (delta.$textOp.check(op)) { - insertText(transaction, this, currPos, op.insert, op.format || {}) + insertText(transaction, /** @type {any} */ (this), currPos, op.insert, op.format || {}) } else if (delta.$insertOp.check(op)) { for (let i = 0; i < op.insert.length; i++) { let ins = op.insert[i] @@ -720,15 +719,15 @@ export class AbstractType { error.unexpectedCase() } } - insertText(transaction, this, currPos, ins, op.format || {}) + insertText(transaction, /** @type {any} */ (this), currPos, ins, op.format || {}) } } else if (delta.$retainOp.check(op)) { - currPos.formatText(transaction, this, op.retain, op.format || {}) + currPos.formatText(transaction, /** @type {any} */ (this), op.retain, op.format || {}) } else if (delta.$deleteOp.check(op)) { deleteText(transaction, currPos, op.delete) } else if (delta.$modifyOp.check(op)) { /** @type {ContentType} */ (currPos.right?.content).type.applyDelta(op.modify) - currPos.formatText(transaction, this, 1, op.format || {}) + currPos.formatText(transaction, /** @type {any} */ (this), 1, op.format || {}) } } }) @@ -1319,7 +1318,7 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deep, modified, de } const prevValue = (prevContentItem !== item && itemsToRender?.hasId(prevContentItem.lastId)) ? array.last(prevContentItem.content.getContent()) : undefined if (deep && c instanceof AbstractType) { - c = c.getContent(am) + c = /** @type {any} */(c).getContent(am) } d.set(key, c, attribution, prevValue) } diff --git a/src/types/YArray.js b/src/types/YArray.js index af4e7b7ba..82d173664 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -30,6 +30,8 @@ import * as delta from 'lib0/delta' // eslint-disable-line * @extends {AbstractType,YArray>} * @implements {Iterable} */ +// @todo remove this +// @ts-ignore export class YArray extends AbstractType { constructor () { super() diff --git a/src/types/YText.js b/src/types/YText.js index 8c035e603..dc50565fe 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -33,7 +33,6 @@ import * as math from 'lib0/math' import * as traits from 'lib0/traits' import * as map from 'lib0/map' import * as error from 'lib0/error' -import * as delta from 'lib0/delta' export class ItemTextListPosition { /** @@ -634,7 +633,7 @@ export const deleteText = (transaction, currPos, length) => { * like pictures and videos), and text formats (**bold**, *italic*). * * @template {{ [key:string]:any } | import('../utils/types.js').YType} [Embeds={ [key:string]:any } | import('../utils/types.js').YType] - * @extends {AbstractType>} + * @extends {AbstractType>} */ export class YText extends AbstractType { /** diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index ca25fabaa..5ee297c81 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -5,16 +5,12 @@ import { AbstractAttributionManager, Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line } from '../internals.js' -import * as array from 'lib0/array' -import * as error from 'lib0/error' import * as delta from 'lib0/delta' // eslint-disable-line /** * @typedef {import('./types.js').YType} _YType */ -const errorComputeChanges = 'You must not compute changes after the event-handler fired.' - /** * @template {_YType} Target * YEvent describes the changes on a YType. diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 1e16f514b..3cb74e842 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -332,7 +332,6 @@ export const testObfuscateUpdates = _tc => { // test ytext const d = /** @type {any} */ (otext.getContent().toJSON().children) t.assert(d.length === 2) - const q = d[0] t.assert(d[0].insert !== 'text' && d[0].insert.length === 4) t.assert(object.length(d[0].format) === 1) t.assert(!object.hasProperty(d[0].format, 'bold')) From 19dbe61141c21c6cc5dbe834a1bda4480a4b7f3c Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 23 Oct 2025 13:54:47 +0200 Subject: [PATCH 355/362] 14.0.0-9 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f7da2ba15..9d1c467c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-8", + "version": "14.0.0-9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-8", + "version": "14.0.0-9", "license": "MIT", "dependencies": { "lib0": "^0.2.115-0" diff --git a/package.json b/package.json index 554ea7f93..dd68affde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-8", + "version": "14.0.0-9", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 584ad5fc9a330b659ed965b6d391234deb2240b9 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 28 Oct 2025 17:51:10 +0100 Subject: [PATCH 356/362] bump deps and use new delta format --- package-lock.json | 4016 +++++++++++++++++++--------------- package.json | 13 +- src/types/AbstractType.js | 6 +- src/types/YXmlElement.js | 2 +- src/utils/types.js | 1 - tests/compatibility.tests.js | 4 +- tests/testHelper.js | 4 +- tests/undo-redo.tests.js | 2 + tests/updates.tests.js | 12 +- tests/y-array.tests.js | 4 +- tests/y-map.tests.js | 15 +- tests/y-text.tests.js | 159 +- tests/y-xml.tests.js | 6 +- 13 files changed, 2382 insertions(+), 1862 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d1c467c8..b17f1e3c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,17 +9,15 @@ "version": "14.0.0-9", "license": "MIT", "dependencies": { - "lib0": "^0.2.115-0" + "lib0": "^0.2.115-1" }, "devDependencies": { "@types/node": "^22.14.1", "@y/protocols": "^1.0.6-1", - "concurrently": "^3.6.1", - "jsdoc": "^3.6.7", - "markdownlint-cli": "^0.41.0", - "rollup": "^4.37.0", - "standard": "^16.0.4", - "tui-jsdoc-template": "^1.2.2", + "concurrently": "^9.2.1", + "markdownlint-cli": "^0.45.0", + "rollup": "^4.52.5", + "standard": "^17.1.2", "typescript": "^5.9.3" }, "engines": { @@ -31,139 +29,109 @@ "url": "https://github.com/sponsors/dmonad" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "bin": { - "parser": "bin/babel-parser.js" + "funding": { + "url": "https://opencollective.com/eslint" }, - "engines": { - "node": ">=6.0.0" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", - "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">= 4" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=10.10.0" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -176,6 +144,51 @@ "node": "*" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -194,21 +207,258 @@ "node": ">=12" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">=14" + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" } }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", - "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", "cpu": [ "x64" ], @@ -219,6 +469,107 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -233,41 +584,44 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", "dev": true, "license": "MIT" }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "22.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", - "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", + "version": "22.18.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.12.tgz", + "integrity": "sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, "node_modules/@y/protocols": { "version": "1.0.6-1", "resolved": "https://registry.npmjs.org/@y/protocols/-/protocols-1.0.6-1.tgz", @@ -312,9 +666,9 @@ } }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -351,20 +705,10 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -375,16 +719,19 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { @@ -423,9 +770,52 @@ "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -472,6 +862,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/arraybuffer.prototype.slice": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", @@ -494,16 +901,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -537,28 +934,38 @@ "dev": true, "license": "MIT" }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/builtins": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", + "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "semver": "^7.0.0" + } + }, + "node_modules/builtins/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/call-bind": { @@ -621,117 +1028,175 @@ "node": ">=6" } }, - "node_modules/catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "lodash": "^4.17.15" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chalk/node_modules/has-flag": { + "node_modules/character-entities-legacy": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", "dev": true, "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/chalk/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha512-8/MzidM6G/TgRelkzDG13y3Y9LxBjCb+8yOEZ9+wwq5gVF2w2pV0wmHvjfT0RvuxGyR7UEuK36r+yYMbT4uKgA==", + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/cheerio/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "BSD-2-Clause" + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, "node_modules/commander": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha512-PhbTMT+ilDXZKqH8xbvuUY2ZEQNef0Q7DKxgoEKb4ccytsdvVVJmYqR0sGbi96nxU6oGrwEIQnclpK2NBZuQlg==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6.x" + "node": ">=18" } }, "node_modules/concat-map": { @@ -742,28 +1207,28 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.1.tgz", - "integrity": "sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^2.4.1", - "commander": "2.6.0", - "date-fns": "^1.23.0", - "lodash": "^4.5.1", - "read-pkg": "^3.0.0", - "rx": "2.3.24", - "spawn-command": "^0.0.2-1", - "supports-color": "^3.2.3", - "tree-kill": "^1.1.0" + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" }, "bin": { - "concurrent": "src/main.js", - "concurrently": "src/main.js" + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" }, "engines": { - "node": ">=4.0.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, "node_modules/cross-spawn": { @@ -781,29 +1246,6 @@ "node": ">= 8" } }, - "node_modules/css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==", - "dev": true, - "license": "BSD-like", - "dependencies": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "node_modules/css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": "*" - } - }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -858,17 +1300,10 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/date-fns": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", - "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", - "dev": true, - "license": "MIT" - }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -883,6 +1318,20 @@ } } }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -936,144 +1385,89 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", - "dependencies": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "1" - } - }, - "node_modules/domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", - "dev": true, - "dependencies": { - "dom-serializer": "0", - "domelementtype": "1" + "engines": { + "node": ">=6" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "dequal": "^2.0.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" + "esutils": "^2.0.2" }, "engines": { - "node": ">=8.6" - } - }, - "node_modules/enquirer/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/enquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1169,6 +1563,34 @@ "node": ">= 0.4" } }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -1229,76 +1651,90 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", - "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.3.0", - "ajv": "^6.10.0", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-config-standard": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", - "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", + "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", "dev": true, "funding": [ { @@ -1315,17 +1751,20 @@ } ], "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-import": "^2.22.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^4.2.1 || ^5.0.0" + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", + "eslint-plugin-promise": "^6.0.0" } }, "node_modules/eslint-config-standard-jsx": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-10.0.0.tgz", - "integrity": "sha512-hLeA2f5e06W1xyr/93/QJulN/rLbUVUmqTlexv9PRKHFwEC9ffJcH2LvJhMoEqYQBEYafedgGZXH2W8NUpt5lA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-11.0.0.tgz", + "integrity": "sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==", "dev": true, "funding": [ { @@ -1343,8 +1782,8 @@ ], "license": "MIT", "peerDependencies": { - "eslint": "^7.12.1", - "eslint-plugin-react": "^7.21.5" + "eslint": "^8.8.0", + "eslint-plugin-react": "^7.28.0" } }, "node_modules/eslint-import-resolver-node": { @@ -1398,9 +1837,9 @@ } }, "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1417,55 +1856,74 @@ "eslint": ">=4.19.1" } }, - "node_modules/eslint-plugin-import": { - "version": "2.24.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", - "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "license": "MIT", "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", - "has": "^1.0.3", - "is-core-module": "^2.6.0", - "minimatch": "^3.0.4", - "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", - "resolve": "^1.20.0", - "tsconfig-paths": "^3.11.0" + "eslint-visitor-keys": "^1.1.0" }, "engines": { - "node": ">=4" + "node": ">=6" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + "funding": { + "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "node_modules/eslint-plugin-import/node_modules/doctrine": { @@ -1494,46 +1952,43 @@ "node": "*" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "node_modules/eslint-plugin-n": { + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", + "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "dev": true, "license": "MIT", "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" + "is-core-module": "^2.11.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.8" }, "engines": { - "node": ">=8.10.0" + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { - "eslint": ">=5.16.0" + "eslint": ">=7.0.0" } }, - "node_modules/eslint-plugin-node/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/eslint-plugin-n/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">= 4" } }, - "node_modules/eslint-plugin-node/node_modules/minimatch": { + "node_modules/eslint-plugin-n/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", @@ -1546,66 +2001,66 @@ "node": "*" } }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/eslint-plugin-n/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/eslint-plugin-promise": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-5.1.1.tgz", - "integrity": "sha512-XgdcdyNzHfmlQyweOPTxmc7pIsS6dE4MvwhXWMQ2Dxs1XAL2GJDilUsjWen6TWik0aSI+zD/PqocZBblcm9rdA==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", + "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "license": "ISC", "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "eslint": "^7.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/eslint-plugin-react": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.25.3.tgz", - "integrity": "sha512-ZMbFvZ1WAYSZKY662MBVEWR45VaBT6KSJCiupjrNlcdakB90juaZeDCbJq19e73JZQubqFtgETohwgAt8u5P6w==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flatmap": "^1.2.4", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "estraverse": "^5.2.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.0.4", - "object.entries": "^1.1.4", - "object.fromentries": "^2.0.4", - "object.hasown": "^1.0.0", - "object.values": "^1.1.4", - "prop-types": "^15.7.2", - "resolve": "^2.0.0-next.3", - "string.prototype.matchall": "^4.0.5" + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7" - } - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -1653,153 +2108,68 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" } }, "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/eslint-visitor-keys": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=7.0.0" + "node": ">=10" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "MIT" + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/eslint/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { @@ -1807,29 +2177,15 @@ } }, "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1843,19 +2199,6 @@ "node": "*" } }, - "node_modules/eslint/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1869,56 +2212,22 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "engines": { - "node": ">=4" + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { @@ -1988,22 +2297,15 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } }, "node_modules/file-entry-cache": { "version": "6.0.1", @@ -2019,16 +2321,20 @@ } }, "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^2.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -2093,6 +2399,21 @@ "dev": true, "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2124,13 +2445,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true, - "license": "MIT" - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -2141,6 +2455,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2181,13 +2515,13 @@ } }, "node_modules/get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2212,47 +2546,50 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dev": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^0.8.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=8" @@ -2298,15 +2635,12 @@ "dev": true, "license": "ISC" }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } + "license": "MIT" }, "node_modules/has-bigints": { "version": "1.1.0", @@ -2322,13 +2656,13 @@ } }, "node_modules/has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/has-property-descriptors": { @@ -2402,39 +2736,10 @@ "node": ">= 0.4" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, - "node_modules/htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -2512,6 +2817,32 @@ "node": ">= 0.4" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -2654,6 +2985,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2691,14 +3033,15 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" }, @@ -2722,6 +3065,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -2765,6 +3119,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -2934,20 +3298,38 @@ "url": "https://github.com/sponsors/dmonad" } }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-tokens": { @@ -2970,56 +3352,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, - "node_modules/jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@babel/parser": "^7.9.4", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3062,9 +3394,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true, "license": "MIT" }, @@ -3091,7 +3423,34 @@ "object.values": "^1.1.6" }, "engines": { - "node": ">=4.0" + "node": ">=4.0" + } + }, + "node_modules/katex": { + "version": "0.16.25", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz", + "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" } }, "node_modules/keyv": { @@ -3104,16 +3463,6 @@ "json-buffer": "3.0.1" } }, - "node_modules/klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3129,9 +3478,9 @@ } }, "node_modules/lib0": { - "version": "0.2.115-0", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.115-0.tgz", - "integrity": "sha512-HAO2E6TJYi3c4DM9x0Ii24+ctnwTYMl/ZOacWB2XvGM2IljHRLzqynl9Voov3TpaO7c99ZQ79O8G7VX6PKP5IQ==", + "version": "0.2.115-1", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.115-1.tgz", + "integrity": "sha512-mmQ4Pk/wZBsjdGMUJtXBhPsqPZof6Eh9sqrApA2Ufqe2eFYWW4yQPZWdf5/ak+dXsRlbslLHrGAn7+MeOY3TGA==", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" @@ -3150,354 +3499,719 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.1.15", "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" }, "engines": { - "node": ">=4" + "node": ">=6" + } + }, + "node_modules/load-json-file/node_modules/type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=6" } }, "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, - "node_modules/lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha512-yX/rx6d/UTVh7sSVWVSIMjfnz95evAgDFdb1ZozC35I9mSFCkmzptOzevxjgbQUsc78NR44LVHWjsoMQXy9FDg==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } }, - "node_modules/lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha512-lxdsn7xxlCymgLYo1gGvVrfHmkjDiyqVv62FAeF2i5ta72BipE1SLxw8hPEPLhD4/247Ijw07UQH7Hq/chT5LA==", + "node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", "dev": true, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } }, - "node_modules/lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha512-pXYUy7PR8BCLwX5mgJ/aNtyOvuJTdZAo9EQFUvMIYugqmJxnrYaANvTbgndOzHSCSR0wnlBBfRXJL5SbWxo3FQ==", + "node_modules/markdownlint": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz", + "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "node_modules/markdownlint-cli": { + "version": "0.45.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.45.0.tgz", + "integrity": "sha512-GiWr7GfJLVfcopL3t3pLumXCYs8sgWppjIA1F/Cc3zIMgD3tmkpyZ1xkm1Tej8mw53B93JsDjgA3KOftuYcfOw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "commander": "~13.1.0", + "glob": "~11.0.2", + "ignore": "~7.0.4", + "js-yaml": "~4.1.0", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdown-it": "~14.1.0", + "markdownlint": "~0.38.0", + "minimatch": "~10.0.1", + "run-con": "~1.3.2", + "smol-toml": "~1.3.4" + }, + "bin": { + "markdownlint": "markdownlint.js" + }, + "engines": { + "node": ">=20" + } }, - "node_modules/lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha512-6raRe2vxCYBhpBu+B+TtNGUzah+hQjVdu3E17wfusjyrXBka2nBS8OH/gjVZ5PvHOhWmIZTYri09Z6n/QfnNMw==", + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha512-qkTuvgEzYdyhiJBx42YPzPo71R1aEr0z79kAv7Ixg8wPFEjgRgJdUsGMG3Hf3OYSF/kHI79XhNlt+5Ar6OzwxQ==", + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", "dev": true, - "license": "ISC" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/markdown-it-anchor": { - "version": "8.6.7", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", - "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", "dev": true, - "license": "Unlicense", - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/markdownlint": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.34.0.tgz", - "integrity": "sha512-qwGyuyKwjkEMOJ10XN6OTKNOVYvOIi35RNvDLNxTof5s8UmyGHlCdpngRHoRGNvQVGuxO3BJ7uNSgdeX166WXw==", + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "markdown-it": "14.1.0", - "markdownlint-micromark": "0.1.9" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/markdownlint-cli": { - "version": "0.41.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.41.0.tgz", - "integrity": "sha512-kp29tKrMKdn+xonfefjp3a/MsNzAd9c5ke0ydMEI9PR98bOjzglYN4nfMSaIs69msUf1DNkgevAIAPtK2SeX0Q==", + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "commander": "~12.1.0", - "get-stdin": "~9.0.0", - "glob": "~10.4.1", - "ignore": "~5.3.1", - "js-yaml": "^4.1.0", - "jsonc-parser": "~3.2.1", - "jsonpointer": "5.0.1", - "markdownlint": "~0.34.0", - "minimatch": "~9.0.4", - "run-con": "~1.3.2", - "smol-toml": "~1.2.0" - }, - "bin": { - "markdownlint": "markdownlint.js" - }, - "engines": { - "node": ">=18" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/markdownlint-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/markdownlint-micromark": { - "version": "0.1.9", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.9.tgz", - "integrity": "sha512-5hVs/DzAFa8XqYosbEAEg6ok6MF2smDj89ztn9pKkCtdKHVdPQuGMH7frFfYL9mLkvfFe4pTyAMffLbjf3/EyA==", + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/markdownlint/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/markdownlint/node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "uc.micro": "^2.0.0" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/markdownlint/node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" + "micromark-util-types": "^2.0.0" } }, - "node_modules/markdownlint/node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "dev": true, - "license": "MIT" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } }, - "node_modules/markdownlint/node_modules/uc.micro": { + "node_modules/micromark-util-subtokenize": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT" }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3523,19 +4237,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3550,29 +4251,6 @@ "dev": true, "license": "MIT" }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "~1.0.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3662,22 +4340,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "license": "MIT", "dependencies": { + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { @@ -3746,39 +4421,45 @@ } }, "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^1.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^1.1.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/package-json-from-dist": { @@ -3801,6 +4482,26 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -3816,13 +4517,13 @@ } }, "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/path-is-absolute": { @@ -3853,50 +4554,30 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/pkg-conf": { @@ -3911,33 +4592,16 @@ }, "engines": { "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/load-json-file": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", - "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^4.0.0", - "pify": "^4.0.1", - "strip-bom": "^3.0.0", - "type-fest": "^0.3.0" + "locate-path": "^3.0.0" }, "engines": { "node": ">=6" @@ -3986,45 +4650,12 @@ "node": ">=6" } }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-conf/node_modules/type-fest": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", - "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha512-fjAPuiws93rm7mPUu21RdBnkeZNrbfCFCwfAhPWY+rR3zG0ubpe5cEReHOw5fIbfmsxEV/g2kSxGTATY3Bpnwg==", + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "license": "MIT", - "dependencies": { - "find-up": "^2.1.0" - }, "engines": { "node": ">=4" } @@ -4049,16 +4680,6 @@ "node": ">= 0.8.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -4091,6 +4712,27 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -4098,50 +4740,6 @@ "dev": true, "license": "MIT" }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4199,34 +4797,24 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/requizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", - "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4250,6 +4838,17 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -4267,17 +4866,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/rimraf/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4314,9 +4902,9 @@ } }, "node_modules/rollup": { - "version": "4.45.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", - "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", "dev": true, "license": "MIT", "dependencies": { @@ -4330,26 +4918,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.1", - "@rollup/rollup-android-arm64": "4.45.1", - "@rollup/rollup-darwin-arm64": "4.45.1", - "@rollup/rollup-darwin-x64": "4.45.1", - "@rollup/rollup-freebsd-arm64": "4.45.1", - "@rollup/rollup-freebsd-x64": "4.45.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", - "@rollup/rollup-linux-arm-musleabihf": "4.45.1", - "@rollup/rollup-linux-arm64-gnu": "4.45.1", - "@rollup/rollup-linux-arm64-musl": "4.45.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-gnu": "4.45.1", - "@rollup/rollup-linux-riscv64-musl": "4.45.1", - "@rollup/rollup-linux-s390x-gnu": "4.45.1", - "@rollup/rollup-linux-x64-gnu": "4.45.1", - "@rollup/rollup-linux-x64-musl": "4.45.1", - "@rollup/rollup-win32-arm64-msvc": "4.45.1", - "@rollup/rollup-win32-ia32-msvc": "4.45.1", - "@rollup/rollup-win32-x64-msvc": "4.45.1", + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" } }, @@ -4369,11 +4959,39 @@ "run-con": "cli.js" } }, - "node_modules/rx": { - "version": "2.3.24", - "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", - "integrity": "sha512-Ue4ZB7Dzbn2I9sIj8ws536nOP2S53uypyCkCz9q0vlYD5Kn6/pu4dE+wt2ZfFzd9m73hiYKnnCb1OyKqc+MRkg==", - "dev": true + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } }, "node_modules/safe-array-concat": { "version": "1.1.3", @@ -4395,27 +5013,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -4452,13 +5049,13 @@ } }, "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" } }, "node_modules/set-function-length": { @@ -4533,6 +5130,19 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -4622,123 +5232,23 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/smol-toml": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.2.2.tgz", - "integrity": "sha512-fVEjX2ybKdJKzFL46VshQbj9PuA4IUKivalgp48/3zwS9vXzyykzQ6AX92UxHSvWJagziMRLeHMgEzoGO7A8hQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz", + "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" } }, - "node_modules/spawn-command": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/standard": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/standard/-/standard-16.0.4.tgz", - "integrity": "sha512-2AGI874RNClW4xUdM+bg1LRXVlYLzTNEkHmTG5mhyn45OhbgwA+6znowkOGYy+WMb5HRyELvtNy39kcdMQMcYQ==", + "version": "17.1.2", + "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.2.tgz", + "integrity": "sha512-WLm12WoXveKkvnPnPnaFUUHuOB2cUdAsJ4AiGHL2G0UNMrcRAWY2WriQaV8IQ3oRmYr0AWUbLNr94ekYFAHOrA==", "dev": true, "funding": [ { @@ -4756,26 +5266,27 @@ ], "license": "MIT", "dependencies": { - "eslint": "~7.18.0", - "eslint-config-standard": "16.0.3", - "eslint-config-standard-jsx": "10.0.0", - "eslint-plugin-import": "~2.24.2", - "eslint-plugin-node": "~11.1.0", - "eslint-plugin-promise": "~5.1.0", - "eslint-plugin-react": "~7.25.1", - "standard-engine": "^14.0.1" + "eslint": "^8.41.0", + "eslint-config-standard": "17.1.0", + "eslint-config-standard-jsx": "^11.0.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-n": "^15.7.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.36.1", + "standard-engine": "^15.1.0", + "version-guard": "^1.1.1" }, "bin": { - "standard": "bin/cmd.js" + "standard": "bin/cmd.cjs" }, "engines": { - "node": ">=10.12.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/standard-engine": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-14.0.1.tgz", - "integrity": "sha512-7FEzDwmHDOGva7r9ifOzD3BGdTbA7ujJ50afLVdW/tK14zQEptJjbFuUfn50irqdHDcTbNh0DTIoMPynMCXb0Q==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-15.1.0.tgz", + "integrity": "sha512-VHysfoyxFu/ukT+9v49d4BRXIokFRZuH3z1VRxzFArZdjSCFpro6rEIU3ji7e4AoAtuSfKBkiOmsrDqKW5ZSRw==", "dev": true, "funding": [ { @@ -4790,29 +5301,16 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT", - "dependencies": { - "get-stdin": "^8.0.0", - "minimist": "^1.2.5", - "pkg-conf": "^3.1.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8.10" - } - }, - "node_modules/standard-engine/node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true, + ], "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "get-stdin": "^8.0.0", + "minimist": "^1.2.6", + "pkg-conf": "^3.1.0", + "xdg-basedir": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/stop-iteration-iterator": { @@ -4829,16 +5327,6 @@ "node": ">= 0.4" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -4931,6 +5419,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -4991,9 +5490,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", "dependencies": { @@ -5054,16 +5553,19 @@ } }, "node_modules/supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^1.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -5079,98 +5581,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/table": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", - "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/table/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/table/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha512-y3JaeRSplks6NYQuCOj3ZFMO3j60rTwbuKCvZxsAraGYH2epusatvZ0baZYA01WsGqJBq/Dl6vOrMUJqyMj8kA==", - "dev": true - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -5201,15 +5611,12 @@ "strip-bom": "^3.0.0" } }, - "node_modules/tui-jsdoc-template": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tui-jsdoc-template/-/tui-jsdoc-template-1.2.2.tgz", - "integrity": "sha512-oqw0IYaot86VJ2owKBozJnilgta0Z55x8r9PeHj7vb+jDoSvJGRUQUcgs56SZh9HE20fx54Pe75p84X85/ygLA==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "MIT", - "dependencies": { - "cheerio": "^0.22.0" - } + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -5225,13 +5632,16 @@ } }, "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typed-array-buffer": { @@ -5327,9 +5737,9 @@ } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "dev": true, "license": "MIT" }, @@ -5352,13 +5762,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore": { - "version": "1.13.7", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", - "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", - "dev": true, - "license": "MIT" - }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -5376,29 +5779,14 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-compile-cache": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", - "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", - "dev": true, - "license": "MIT" - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/version-guard": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/version-guard/-/version-guard-1.1.3.tgz", + "integrity": "sha512-JwPr6erhX53EWH/HCSzfy1tTFrtPXUe927wdM1jqBBeYp1OM+qPHjWbsvv6pIBduqdgxxS+ScfG7S28pzyr2DQ==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "0BSD", + "engines": { + "node": ">=0.10.48" } }, "node_modules/which": { @@ -5563,42 +5951,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -5635,9 +5987,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { @@ -5664,16 +6016,144 @@ "node": ">=8" } }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "Apache-2.0" + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, "node_modules/yjs": { - "resolved": "", - "link": true + "version": "13.6.27", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", + "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/yjs/node_modules/lib0": { + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index dd68affde..7a2ee0dd8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "dist": "npm run clean && rollup -c && tsc --skipLibCheck", "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 && 0serve ./docs/", "preversion": "PRODUCTION=1 npm run dist && npm run docs && 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", @@ -85,17 +84,15 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.115-0" + "lib0": "^0.2.115-1" }, "devDependencies": { "@types/node": "^22.14.1", "@y/protocols": "^1.0.6-1", - "concurrently": "^3.6.1", - "jsdoc": "^3.6.7", - "markdownlint-cli": "^0.41.0", - "rollup": "^4.37.0", - "standard": "^16.0.4", - "tui-jsdoc-template": "^1.2.2", + "concurrently": "^9.2.1", + "markdownlint-cli": "^0.45.0", + "rollup": "^4.52.5", + "standard": "^17.1.2", "typescript": "^5.9.3" }, "engines": { diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 461c47924..d608b468d 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -468,7 +468,7 @@ export class AbstractType { getContent (am = noAttributionsManager, opts = {}) { const { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null, modified = null, deep = false } = opts /** - * @type {EventDelta} + * @type {EventDelta extends delta.Delta ? delta.DeltaBuilder : never} */ const d = /** @type {any} */ (delta.create(/** @type {any} */ (this).nodeName || null)) typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deep, modified, deletedItems, itemsToRender) @@ -726,7 +726,7 @@ export class AbstractType { } else if (delta.$deleteOp.check(op)) { deleteText(transaction, currPos, op.delete) } else if (delta.$modifyOp.check(op)) { - /** @type {ContentType} */ (currPos.right?.content).type.applyDelta(op.modify) + /** @type {ContentType} */ (currPos.right?.content).type.applyDelta(op.value) currPos.formatText(transaction, /** @type {any} */ (this), 1, op.format || {}) } } @@ -1277,7 +1277,7 @@ export const typeMapGetAll = (parent) => { * Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the * attribution `{ isDeleted: true, .. }`. * - * @template {delta.Delta} TypeDelta + * @template {delta.DeltaBuilder} TypeDelta * @param {TypeDelta} d * @param {YType_} parent * @param {Set?} attrsToRender diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 24e2ffd3e..83a3aa4ac 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -24,7 +24,7 @@ import { * * An YXmlElement has attributes (key value pairs) * * An YXmlElement has childElements that must inherit from YXmlElement * - * @template {{ [key: string]: ValueTypes }} [Attrs={ [key: string]: string }] + * @template {{ [key: string]: any }} [Attrs={ [key: string]: string }] * @template {any} [Children=any] * @extends YXmlFragment */ diff --git a/src/utils/types.js b/src/utils/types.js index f18a546af..f2cc7747c 100644 --- a/src/utils/types.js +++ b/src/utils/types.js @@ -1,4 +1,3 @@ - /** * @typedef {import('../types/YArray.js').YArray * | import('../types/YMap.js').YMap diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 513d347f2..98d97e92c 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -38,8 +38,8 @@ export const testMapDecodingCompatibilityV1 = _tc => { export const testTextDecodingCompatibilityV1 = _tc => { const oldDoc = 'BS8EAAUBBHRleHRveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9RAQAATHBBAEEAAHBBAIEAAHEBAMEAAQxdXUKxQQCBANveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAlveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xgMBAwIGaXRhbGljBHRydWXGBAsDAgVjb2xvcgYiIzg4OCLEBAwDAgExxAQNAwIBMsEEDgMCAsYEEAMCBml0YWxpYwRudWxsxgQRAwIFY29sb3IEbnVsbMQDAQQLATHEBBMECwIyOcQEFQQLCzl6anpueXdvaHB4xAQgBAsIY25icmNhcQrBAxADEQHGAR8BIARib2xkBHRydWXGAgACAQRib2xkBG51bGzFAwkECm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GARABEQZpdGFsaWMEdHJ1ZcYELQERBWNvbG9yBiIjODg4IsYBEgETBml0YWxpYwRudWxsxgQvARMFY29sb3IEbnVsbMYCKwIsBGJvbGQEdHJ1ZcYCLQIuBGJvbGQEbnVsbMYCjAECjQEGaXRhbGljBHRydWXGAo4BAo8BBml0YWxpYwRudWxswQA2ADcBxgQ1ADcFY29sb3IGIiM4ODgixgNlA2YFY29sb3IEbnVsbMYDUwNUBGJvbGQEdHJ1ZcQEOANUFjEzMTZ6bHBrbWN0b3FvbWdmdGhicGfGBE4DVARib2xkBG51bGzGAk0CTgZpdGFsaWMEdHJ1ZcYEUAJOBWNvbG9yBiIjODg4IsYCTwJQBml0YWxpYwRudWxsxgRSAlAFY29sb3IEbnVsbMYChAEChQEGaXRhbGljBHRydWXGBFQChQEFY29sb3IGIiM4ODgixgKGAQKHAQZpdGFsaWMEbnVsbMYEVgKHAQVjb2xvcgRudWxsxAMpAyoRMTMyMWFwZ2l2eWRxc2pmc2XFBBIDAm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn0zAwAEAQR0ZXh0AjEyhAMBAzkwboQDBAF4gQMFAoQDBwJyCsQDBAMFBjEyOTd6bcQDDwMFAXbEAxADBQFwwQMRAwUBxAMSAwUFa3pxY2rEAxcDBQJzYcQDGQMFBHNqeQrBAxIDEwHBAAwAEAHEAA0ADgkxMzAyeGNpd2HEAygADgF5xAMpAA4KaGhlenVraXF0dMQDMwAOBWhudGsKxgMoAykEYm9sZAR0cnVlxAM5AykGMTMwNXJswQM/AykCxANBAykDZXlrxgNEAykEYm9sZARudWxsxAMzAzQJMTMwN3R2amllwQNOAzQCxANQAzQDamxoxANTAzQCZ3bEA1UDNAJsYsQDVwM0AmYKxgNBA0IEYm9sZARudWxswQNaA0ICxANcA0ICMDjBA14DQgLEA2ADQgEKxgNhA0IEYm9sZAR0cnVlxQIaAhtveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9wQA3ADgCwQNlADgBxANmADgKMTVteml3YWJ6a8EDcAA4AsQDcgA4BnJybXNjdsEDeAA4AcQCYgJjATHEA3oCYwIzMsQDfAJjCTRyb3J5d3RoccQDhQECYwEKxAOFAQOGARkxMzI1aW9kYnppenhobWxpYnZweXJ4bXEKwQN6A3sBxgOgAQN7BWNvbG9yBiIjODg4IsYDfAN9Bml0YWxpYwRudWxsxgOiAQN9BWNvbG9yBG51bGxSAgAEAQR0ZXh0ATGEAgACMjiEAgIBOYECAwKEAgUBdYQCBgJ0Y4QCCAJqZYECCgKEAgwBaoECDQGBAg4BhAIPAnVmhAIRAQrEAg4CDwgxMjkycXJtZsQCGgIPAmsKxgIGAgcGaXRhbGljBHRydWXGAggCCQZpdGFsaWMEbnVsbMYCEQISBml0YWxpYwR0cnVlxAIfAhIBMcECIAISAsQCIgISAzRoc8QCJQISAXrGAiYCEgZpdGFsaWMEbnVsbMEAFQAWAsQCKQAWATDEAioAFgEwxAIrABYCaHjEAi0AFglvamVldHJqaHjBAjYAFgLEAjgAFgJrcsQCOgAWAXHBAjsAFgHBAjwAFgHEAj0AFgFuxAI+ABYCZQrGAiUCJgZpdGFsaWMEbnVsbMQCQQImAjEzwQJDAiYCxAJFAiYIZGNjeGR5eGfEAk0CJgJ6Y8QCTwImA2Fwb8QCUgImAnRuxAJUAiYBcsQCVQImAmduwQJXAiYCxAJZAiYBCsYCWgImBml0YWxpYwR0cnVlxAI6AjsEMTMwM8QCXwI7A3VodsQCYgI7BmdhbmxuCsUCVQJWb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcECPAI9AcECPgI/AcYDFwMYBml0YWxpYwR0cnVlxgJsAxgFY29sb3IGIiM4ODgixgMZAxoGaXRhbGljBG51bGzGAm4DGgVjb2xvcgRudWxswQMQBCkBxAJwBCkKMTMwOXpsZ3ZqeMQCegQpAWfBAnsEKQLGBA0EDgZpdGFsaWMEbnVsbMYCfgQOBWNvbG9yBG51bGzEAn8EDgUxMzEwZ8QChAEEDgJ3c8QChgEEDgZoeHd5Y2jEAowBBA4Ca3HEAo4BBA4Ec2RydcQCkgEEDgRqcWljwQKWAQQOBMQCmgEEDgEKxgKbAQQOBml0YWxpYwR0cnVlxgKcAQQOBWNvbG9yBiIjODg4IsECaAI7AcQCCgEBFjEzMThqd3NramFiZG5kcmRsbWphZQrGA1UDVgRib2xkBHRydWXGA1cDWARib2xkBG51bGzGAEAAQQZpdGFsaWMEdHJ1ZcYCtwEAQQRib2xkBG51bGzEArgBAEESMTMyNnJwY3pucWFob3BjcnRkxgLKAQBBBml0YWxpYwRudWxsxgLLAQBBBGJvbGQEdHJ1ZRkBAMUCAgIDb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcQCCgILBzEyOTN0agrGABgAGQRib2xkBHRydWXGAA0ADgRib2xkBG51bGxEAgAHMTMwNnJ1cMQBEAIAAnVqxAESAgANaWtrY2pucmNwc2Nrd8QBHwIAAQrFBBMEFG97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3FAx0DBW97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAlICUwRib2xkBHRydWXGAlQCVQRib2xkBG51bGzGAnsCfAZpdGFsaWMEdHJ1ZcYBJQJ8BWNvbG9yBiIjODg4IsYBJgJ8BGJvbGQEbnVsbMQBJwJ8CjEzMTRweWNhdnXGATECfAZpdGFsaWMEbnVsbMYBMgJ8BWNvbG9yBG51bGzBATMCfAHFADEAMm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GADUANgZpdGFsaWMEdHJ1ZcEANwA4AcQAMgAzEzEzMjJybmJhb2tvcml4ZW52cArEAgUCBhcxMzIzbnVjdnhzcWx6bndsZmF2bXBjCsYDDwMQBGJvbGQEdHJ1ZR0AAMQEAwQEDTEyOTVxZnJ2bHlmYXDEAAwEBAFjxAANBAQCanbBAAwADQHEABAADQEywQARAA0ExAAVAA0DZHZmxAAYAA0BYcYCAwIEBml0YWxpYwR0cnVlwQAaAgQCxAAcAgQEMDRrdcYAIAIEBml0YWxpYwRudWxsxQQgBCFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQJAABZveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xAQVBBYGMTMxMWtrxAIqAisIMTMxMnFyd3TEADECKwFixAAyAisDcnhxxAA1AisBasQANgIrAXjEADcCKwZkb3ZhbwrEAgAEKwMxMzHEAEAEKwkzYXhoa3RoaHXGAnoCewRib2xkBG51bGzFAEoCe297ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAEsCewRib2xkBHRydWXEAl8CYBExMzE3cGZjeWhrc3JrcGt0CsQBHwQqCzEzMTliY2Nna3AKxAKSAQKTARUxMzIwY29oYnZjcmtycGpuZ2RvYwoFBAQCAg8CKQE1AQADEAESBBsCAwsGAhIBHgJAAk8CWwJfAmQDcQJ5AaABAQIOBAILAg4CIQIoAjcCPAJEAlgCagJwAXwClwEEngEBAQI0ATcB' // eslint-disable-next-line - const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"291311kk9zjznywohpx"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"xr\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]}] + const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"291311kk9zjznywohpx"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"xr\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]}].map(x => ({ type: 'insert', ...x })) const doc = new Y.Doc() Y.applyUpdate(doc, buffer.fromBase64(oldDoc)) - t.compare(doc.getText('text').getContent().toJSON().children, oldVal) + t.compare(doc.getText('text').getContent().toJSON().children, /** @type {any} */ (oldVal)) } diff --git a/tests/testHelper.js b/tests/testHelper.js index ad11f0129..02fc7bc82 100644 --- a/tests/testHelper.js +++ b/tests/testHelper.js @@ -460,7 +460,7 @@ export const compare = users => { users.push(.../** @type {any} */(mergedDocs)) const userArrayValues = users.map(u => u.getArray('array').toJSON()) const userMapValues = users.map(u => u.getMap('map').toJSON()) - const userXmlValues = users.map(u => u.get('xml', Y.XmlElement).toString()) + const userXmlValues = users.map(u => /** @type {Y.XmlElement} */ (u.get('xml', Y.XmlElement)).toString()) const userTextValues = users.map(u => u.getText('text').getContentDeep()) for (const u of users) { t.assert(u.store.pendingDs === null) @@ -486,7 +486,7 @@ export const compare = users => { t.compare(userArrayValues[i], userArrayValues[i + 1]) t.compare(userMapValues[i], userMapValues[i + 1]) t.compare(userXmlValues[i], userXmlValues[i + 1]) - t.compare(list.toArray(userTextValues[i].children).map(a => delta.$textOp.check(a) ? a.insert : ' ').join('').length, users[i].getText('text').length) + t.compare(list.toArray(userTextValues[i].children).map(a => (delta.$textOp.check(a) || delta.$insertOp.check(a)) ? a.insert.length : 0).reduce((a, b) => a + b, 0), users[i].getText('text').length) t.compare(userTextValues[i], userTextValues[i + 1], '', (_constructor, a, b) => { if (a instanceof Y.AbstractType) { t.compare(a.toJSON(), b.toJSON()) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 4bfd49062..7c9d8f6e4 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -299,7 +299,9 @@ export const testUndoXml = tc => { // format textchild and revert that change undoManager.stopCapturing() textchild.format(3, 4, { bold: {} }) + t.compare(xml0.getContentDeep(), delta.create('UNDEFINED').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done()) t.assert(xml0.toString() === '

content

') + debugger undoManager.undo() t.assert(xml0.toString() === '

content

') undoManager.redo() diff --git a/tests/updates.tests.js b/tests/updates.tests.js index 3cb74e842..8081007c6 100644 --- a/tests/updates.tests.js +++ b/tests/updates.tests.js @@ -127,7 +127,15 @@ export const testKeyEncoding = tc => { const update = Y.encodeStateAsUpdateV2(users[0]) Y.applyUpdateV2(users[1], update) - t.compare(text1.getContent().toJSON().children, [{ insert: 'c', format: { italic: true } }, { insert: 'b' }, { insert: 'a', format: { italic: true } }]) + const c = text1.getContent() + t.compare( + c, + delta.create() + .insert('c', { italic: true }) + .insert('b') + .insert('a', { italic: true }) + .done() + ) compare(users) } @@ -335,7 +343,7 @@ export const testObfuscateUpdates = _tc => { t.assert(d[0].insert !== 'text' && d[0].insert.length === 4) t.assert(object.length(d[0].format) === 1) t.assert(!object.hasProperty(d[0].format, 'bold')) - t.assert(object.length(d[1]) === 1) + t.assert(object.length(d[1].insert) === 1) t.assert(object.hasProperty(d[1], 'insert')) // test ymap t.assert(omap.size === 1) diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index aec28521f..29ccf2927 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -393,13 +393,13 @@ export const testChangeEvent = tc => { const newArr = new Y.Array() array0.insert(0, [newArr, 4, 'dtrn']) t.assert(d !== null && d.children.len === 1) - t.compare(d.toJSON().children, [{ insert: [newArr, 4, 'dtrn'] }]) + t.compare(d, delta.create().insert([newArr, 4, 'dtrn'])) array0.delete(0, 2) t.assert(d !== null && d.children.len === 1) t.compare(d.toJSON().children, [{ delete: 2 }]) array0.insert(1, [0.1]) t.assert(d !== null && d.children.len === 2) - t.compare(d.toJSON().children, [{ retain: 1 }, { insert: [0.1] }]) + t.compare(d, delta.create().retain(1).insert([0.1]).done()) compare(users) } diff --git a/tests/y-map.tests.js b/tests/y-map.tests.js index c86155af7..4fa5e9649 100644 --- a/tests/y-map.tests.js +++ b/tests/y-map.tests.js @@ -10,6 +10,7 @@ import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as delta from 'lib0/delta' import * as s from 'lib0/schema' +import * as object from 'lib0/object' /** * @param {t.TestCase} _tc @@ -498,33 +499,33 @@ export const testChangeEvent = tc => { changes = e.delta }) map0.set('a', 1) - let keyChange = changes.attrs.get('a') + let keyChange = changes.attrs.a t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined) map0.set('a', 2) - keyChange = changes.attrs.get('a') + keyChange = changes.attrs.a t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === 1) users[0].transact(() => { map0.set('a', 3) map0.set('a', 4) }) - keyChange = changes.attrs.get('a') + keyChange = changes.attrs.a t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === 2) users[0].transact(() => { map0.set('b', 1) map0.set('b', 2) }) - keyChange = changes.attrs.get('b') + keyChange = changes.attrs.b t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined) users[0].transact(() => { map0.set('c', 1) map0.delete('c') }) - t.assert(changes !== null && changes.attrs.size === 0) + t.assert(changes !== null && object.isEmpty(changes.attrs)) users[0].transact(() => { map0.set('d', 1) map0.set('d', 2) }) - keyChange = changes.attrs.get('d') + keyChange = changes.attrs.d t.assert(delta.$insertOpWith(s.$number).check(keyChange) && keyChange.prevValue === undefined) compare(users) } @@ -635,7 +636,7 @@ export const testAttributedContent = _tc => { }) t.group('overwrite value', () => { ymap.set('test', 'fourtytwo') - const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'insert', prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } }) } + const expectedContent = { test: delta.$deltaMapChangeJson.expect({ type: 'insert', value: 'fourtytwo', attribution: { insert: [] } }) } const attributedContent = ymap.getContent(attributionManager) console.log(attributedContent) t.compare(expectedContent, attributedContent.toJSON().attrs) diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 8e5fa34a3..583a2e23b 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1216,6 +1216,7 @@ export const testDeltaBug2 = _tc => { ytext.applyDelta(changeEvent) const d = ytext.getContent() t.compare(list.toArray(d.children)[40].toJSON(), { + type: 'insert', insert: '\n', format: { 'block-id': 'block-9d6566a1-be55-4e20-999a-b990bc15e143' @@ -1242,11 +1243,12 @@ export const testDeltaAfterConcurrentFormatting = tc => { const deltas = [] text1.observe(event => { if (event.delta.children.len > 0) { - deltas.push(event.delta.toJSON()) + deltas.push(event.delta) } }) testConnector.flushAllMessages() - t.compare(deltas, [[{ retain: 2, format: { bold: true } }, { retain: 1 }, { retain: 1, format: { bold: null } }]]) + t.assert(deltas.length === 1) + t.compare(deltas[0], delta.create().retain(2, { bold: true }).retain(1).retain(1, { bold: null }).done()) } /** @@ -1295,29 +1297,29 @@ export const testBasicFormat = tc => { }) text0.insert(0, 'abc', { bold: true }) t.assert(text0.toString() === 'abc', 'Basic insert with attributes works') - t.compare(text0.getContent(), delta.create().insert('abc', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('abc', { bold: true }).done()) t.compare(eventDelta, delta.create().insert('abc', { bold: true })) text0.delete(0, 1) t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)') - t.compare(text0.getContent(), delta.create().insert('bc', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('bc', { bold: true }).done()) t.compare(eventDelta, delta.create().delete(1)) text0.delete(1, 1) t.assert(text0.toString() === 'b', 'Basic delete works (position 1)') - t.compare(text0.getContent(), delta.create().insert('b', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('b', { bold: true }).done()) t.compare(eventDelta, delta.create().retain(1).delete(1)) text0.insert(0, 'z', { bold: true }) t.assert(text0.toString() === 'zb') - t.compare(text0.getContent(), delta.create().insert('zb', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('zb', { bold: true }).done()) t.compare(eventDelta, delta.create().insert('z', { bold: true })) // @ts-ignore t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker') text0.insert(0, 'y') t.assert(text0.toString() === 'yzb') - t.compare(text0.getContent(), delta.create().insert('y').insert('zb', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('y').insert('zb', { bold: true }).done()) t.compare(eventDelta, delta.create().insert('y')) text0.format(0, 2, { bold: null }) t.assert(text0.toString() === 'yzb') - t.compare(text0.getContent(), delta.create().insert('yz').insert('b', { bold: true })) + t.compare(text0.getContent(), delta.create().insert('yz').insert('b', { bold: true }).done()) t.compare(eventDelta, delta.create().retain(1).retain(1, { bold: null })) compare(users) } @@ -1332,14 +1334,14 @@ export const testFalsyFormats = tc => { delta = event.delta.toJSON().children }) text0.insert(0, 'abcde', { falsy: false }) - t.compare(text0.getContent().toJSON().children, [{ insert: 'abcde', format: { falsy: false } }]) - t.compare(delta, [{ insert: 'abcde', format: { falsy: false } }]) + t.compare(text0.getContent().toJSON().children, [{ type: 'insert', insert: 'abcde', format: { falsy: false } }]) + t.compare(delta, [{ type: 'insert', insert: 'abcde', format: { falsy: false } }]) text0.format(1, 3, { falsy: true }) - t.compare(text0.getContent().toJSON().children, [{ insert: 'a', format: { falsy: false } }, { insert: 'bcd', format: { falsy: true } }, { insert: 'e', format: { falsy: false } }]) - t.compare(delta, [{ retain: 1 }, { retain: 3, format: { falsy: true } }]) + t.compare(text0.getContent().toJSON().children, [{ type: 'insert', insert: 'a', format: { falsy: false } }, { type: 'insert', insert: 'bcd', format: { falsy: true } }, { type: 'insert', insert: 'e', format: { falsy: false } }]) + t.compare(delta, [{ type: 'retain', retain: 1 }, { type: 'retain', retain: 3, format: { falsy: true } }]) text0.format(2, 1, { falsy: false }) - t.compare(text0.getContent().toJSON().children, [{ insert: 'a', format: { falsy: false } }, { insert: 'b', format: { falsy: true } }, { insert: 'c', format: { falsy: false } }, { insert: 'd', format: { falsy: true } }, { insert: 'e', format: { falsy: false } }]) - t.compare(delta, [{ retain: 2 }, { retain: 1, format: { falsy: false } }]) + t.compare(text0.getContent().toJSON().children, [{ type: 'insert', insert: 'a', format: { falsy: false } }, { type: 'insert', insert: 'b', format: { falsy: true } }, { type: 'insert', insert: 'c', format: { falsy: false } }, { type: 'insert', insert: 'd', format: { falsy: true } }, { type: 'insert', insert: 'e', format: { falsy: false } }]) + t.compare(delta, [{ type: 'retain', retain: 2 }, { type: 'retain', retain: 1, format: { falsy: false } }]) compare(users) } @@ -1357,13 +1359,16 @@ export const testMultilineFormat = _tc => { .retain(1) // newline character .retain(10, { bold: true }) testText.applyDelta(tt) - t.compare(testText.getContent().toJSON().children, [ - { insert: 'Test', format: { bold: true } }, - { insert: '\n' }, - { insert: 'Multi-line', format: { bold: true } }, - { insert: '\n' }, - { insert: 'Formatting', format: { bold: true } } - ]) + t.compare( + testText.getContent(), + delta.create() + .insert('Test', { bold: true }) + .insert('\n') + .insert('Multi-line', { bold: true }) + .insert('\n') + .insert('Formatting', { bold: true }) + .done() + ) } /** @@ -1378,12 +1383,15 @@ export const testNotMergeEmptyLinesFormat = _tc => { .insert('\nText') .insert('\n', { title: true }) ) - t.compare(testText.getContent().toJSON().children, [ - { insert: 'Text' }, - { insert: '\n', format: { title: true } }, - { insert: '\nText' }, - { insert: '\n', format: { title: true } } - ]) + t.compare( + testText.getContent(), + delta.create() + .insert('Text') + .insert('\n', { title: true }) + .insert('\nText') + .insert('\n', { title: true }) + .done() + ) } /** @@ -1402,10 +1410,12 @@ export const testPreserveAttributesThroughDelete = _tc => { .delete(1) .retain(1, { title: true }) ) - t.compare(testText.getContent().toJSON().children, [ - { insert: 'Text' }, - { insert: '\n', format: { title: true } } - ]) + t.compare(testText.getContent(), + delta.create() + .insert('Text') + .insert('\n', { title: true }) + .done() + ) } /** @@ -1414,9 +1424,11 @@ export const testPreserveAttributesThroughDelete = _tc => { export const testGetDeltaWithEmbeds = tc => { const { text0 } = init(tc, { users: 1 }) text0.applyDelta(delta.create().insert([{ linebreak: 's' }])) - t.compare(text0.getContent().toJSON().children, [{ - insert: [{ linebreak: 's' }] - }]) + t.compare(text0.getContent(), + delta.create() + .insert([{ linebreak: 's' }]) + .done() + ) } /** @@ -1427,18 +1439,18 @@ export const testTypesAsEmbed = tc => { text0.applyDelta(delta.create() .insert([new Y.Map([['key', 'val']])]) ) - t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children[0]?.insert, { key: 'val' }) + t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ attrs: { key: { type: 'insert', value: 'val' } }}] }]) let firedEvent = false text1.observe(event => { - const d = event.delta + const d = event.deltaDeep t.assert(d.children.len === 1) - t.compare(list.toArray(d.children).map(x => /** @type {any} */ (x).insert.toJSON()), [{ key: 'val' }]) + t.compare(d.toJSON().children?.map(x => /** @type {any} */ (x).insert), [[{ key: { type: 'insert', value: 'val' } }]]) firedEvent = true }) testConnector.flushAllMessages() const dd = text1.getContent().toJSON().children t.assert(dd?.length === 1) - t.compare(/** @type {any} */ (dd?.[0]).insert.toJSON(), { key: 'val' }) + t.compare(/** @type {any} */ (dd?.[0]).insert, { key: 'val' }) t.assert(firedEvent, 'fired the event observer containing a Type-Embed') } @@ -1463,12 +1475,19 @@ export const testSnapshot = tc => { .delete(1) ) const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1)) - t.compare(state1.toJSON().children, [{ insert: 'abcd' }]) + t.compare(state1, delta.create().insert('abcd').done()) const state2 = text0.getContent(createAttributionManagerFromSnapshots(snapshot2)) - t.compare(state2.toJSON().children, [{ insert: 'axcd' }]) - const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON().children - const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }] - t.compare(state2Diff, expected) + t.compare(state2, delta.create().insert('axcd').done()) + const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)) + t.compare( + state2Diff, + delta.create() + .insert('a') + .insert('x', null, { insert: [] }) + .insert('b', null, { delete: [] }) + .insert('cd') + .done() + ) } /** @@ -1506,8 +1525,15 @@ export const testToDeltaEmbedAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 }) - const delta0 = text0.getContent().toJSON().children - t.compare(delta0, [{ insert: 'a', format: { bold: true } }, { insert: [{ image: 'imageSrc.png' }], format: { width: 100 } }, { insert: 'b', format: { bold: true } }]) + const delta0 = text0.getContent() + t.compare( + delta0, + delta.create() + .insert('a', { bold: true }) + .insert([{ image: 'imageSrc.png' }], { width: 100 }) + .insert('b', { bold: true }) + .done() + ) } /** @@ -1517,8 +1543,16 @@ export const testToDeltaEmbedNoAttributes = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.insertEmbed(1, { image: 'imageSrc.png' }) - const delta0 = text0.getContent().toJSON().children - t.compare(delta0, [{ insert: 'a', format: { bold: true } }, { insert: [{ image: 'imageSrc.png' }] }, { insert: 'b', format: { bold: true } }], 'toDelta does not set attributes key when no attributes are present') + const delta0 = text0.getContent() + t.compare( + delta0, + delta.create() + .insert('a', { bold: true }) + .insert([{ image: 'imageSrc.png' }]) + .insert('b', { bold: true }) + .done() + , 'toDelta does not set attributes key when no attributes are present' + ) } /** @@ -1528,6 +1562,7 @@ export const testFormattingRemoved = tc => { const { text0 } = init(tc, { users: 1 }) text0.insert(0, 'ab', { bold: true }) text0.delete(0, 2) + // @ts-ignore t.assert(Y.getTypeChildren(text0).length === 1) } @@ -1571,9 +1606,7 @@ export const testFormattingDeltaUnnecessaryAttributeChange = tc => { testConnector.flushAllMessages() const filteredDeltas = deltas.filter(d => d.children.len > 0) t.assert(filteredDeltas.length === 2) - t.compare(filteredDeltas[0].toJSON().children, [ - { retain: 1, format: { LIST_STYLES: 'number' } } - ]) + t.compare(filteredDeltas[0], delta.create().retain(1, { LIST_STYLES: 'number' }).done()) t.compare(filteredDeltas[0], filteredDeltas[1]) } @@ -1820,12 +1853,12 @@ export const testFormattingBug = async _tc => { ydoc2.getText().format(1, 1, { url: 'http://docs.yjs.dev' }) Y.applyUpdate(ydoc2, Y.encodeStateAsUpdate(ydoc1)) const text2 = ydoc2.getText() - const expectedResult = [ - { insert: '\n', format: { url: 'http://example.com' } }, - { insert: '\n', format: { url: 'http://docs.yjs.dev' } }, - { insert: '\n', format: { url: 'http://example.com' } } - ] - t.compare(text1.getContent().toJSON().children, expectedResult) + const expectedResult = delta.create() + .insert('\n', { url: 'http://example.com' }) + .insert('\n', { url: 'http://docs.yjs.dev' }) + .insert('\n', { url: 'http://example.com' }) + .done() + t.compare(text1.getContent(), expectedResult) t.compare(text1.getContent().toJSON(), text2.getContent().toJSON()) console.log(text1.getContent().toJSON()) } @@ -1850,13 +1883,13 @@ export const testDeleteFormatting = _tc => { text.format(16, 4, { bold: null }) Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc)) - const expected = [ - { insert: 'Attack ships ' }, - { insert: 'on ', format: { bold: true } }, - { insert: 'fire off the shoulder of Orion.' } - ] - t.compare(text.getContent().toJSON().children, expected) - t.compare(text2.getContent().toJSON().children, expected) + const expected = delta.create() + .insert('Attack ships ') + .insert('on ', { bold: true }) + .insert('fire off the shoulder of Orion.') + .done() + t.compare(text.getContent(), expected) + t.compare(text2.getContent(), expected) } /** diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index c1a3bffbb..038f77bc4 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -106,7 +106,7 @@ export const testYtextAttributes = _tc => { const ydoc = new Y.Doc() const ytext = /** @type {Y.XmlText} */ (ydoc.get('', Y.XmlText)) ytext.observe(event => { - t.assert(event.delta.attrs.get('test')?.type === 'insert') + t.assert(event.delta.attrs.test?.type === 'insert') }) ytext.setAttribute('test', 42) t.compare(ytext.getAttribute('test'), 42) @@ -124,7 +124,7 @@ export const testSiblings = _tc => { yxml.insert(0, [first, second]) t.assert(first.nextSibling === second) t.assert(second.prevSibling === first) - t.assert(first.parent === yxml) + t.assert(first.parent === /** @type {Y.AbstractType} */ (yxml)) t.assert(yxml.parent === null) t.assert(yxml.firstChild === first) } @@ -272,7 +272,7 @@ export const testElementAttributedContent = _tc => { console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.toJSON().attrs) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent, /** @type {delta.MapDelta} */ (delta.map()).set('key', '42', { insert: [] })) + t.compare(attributedContent, delta.map().set('key', '42', { insert: [] })) t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.name === 'UNDEFINED') }) From 029aba44c3158af05c059a20cf3ef620904f0ed6 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 29 Oct 2025 02:39:27 +0100 Subject: [PATCH 357/362] usability - observeDeep returns function --- package.json | 2 +- src/types/AbstractType.js | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7a2ee0dd8..f9db95f44 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "import": "./src/internals.js" }, "./testHelper": { - "types": "./dist/testHelper.d.ts", + "types": "./dist/tests/testHelper.d.ts", "module": "./tests/testHelper.js", "require": "./dist/testHelper.cjs", "import": "./tests/testHelper.js" diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index d608b468d..80da2b68c 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -403,19 +403,25 @@ export class AbstractType { /** * Observe all events that are created on this type. * - * @param {(target: YEvent, tr: Transaction) => void} f Observer function + * @template {(target: YEvent, tr: Transaction) => void} F + * @param {F} f Observer function + * @return {F} */ observe (f) { addEventHandlerListener(this._eH, f) + return f } /** * Observe all events that are created by this type and its children. * - * @param {function(Array>,Transaction):void} f Observer function + * @template {function(Array>,Transaction):void} F + * @param {F} f Observer function + * @return {F} */ observeDeep (f) { addEventHandlerListener(this._dEH, f) + return f } /** @@ -728,6 +734,8 @@ export class AbstractType { } else if (delta.$modifyOp.check(op)) { /** @type {ContentType} */ (currPos.right?.content).type.applyDelta(op.value) currPos.formatText(transaction, /** @type {any} */ (this), 1, op.format || {}) + } else { + error.unexpectedCase() } } }) From 61e345974556551b728395334a1c254f7f1c9e0f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 29 Oct 2025 02:43:08 +0100 Subject: [PATCH 358/362] lint --- tests/undo-redo.tests.js | 1 - tests/y-text.tests.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 7c9d8f6e4..3304c34c4 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -301,7 +301,6 @@ export const testUndoXml = tc => { textchild.format(3, 4, { bold: {} }) t.compare(xml0.getContentDeep(), delta.create('UNDEFINED').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done()) t.assert(xml0.toString() === '

content

') - debugger undoManager.undo() t.assert(xml0.toString() === '

content

') undoManager.redo() diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index 583a2e23b..f846991e2 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1439,7 +1439,7 @@ export const testTypesAsEmbed = tc => { text0.applyDelta(delta.create() .insert([new Y.Map([['key', 'val']])]) ) - t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ attrs: { key: { type: 'insert', value: 'val' } }}] }]) + t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ attrs: { key: { type: 'insert', value: 'val' } } }] }]) let firedEvent = false text1.observe(event => { const d = event.deltaDeep From 54c041c61333467ba96983db70818b8ec94fedf6 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 29 Oct 2025 02:50:00 +0100 Subject: [PATCH 359/362] cleanup docs task --- package.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f9db95f44..76c3eff84 100644 --- a/package.json +++ b/package.json @@ -12,14 +12,13 @@ "url": "https://github.com/sponsors/dmonad" }, "scripts": { - "clean": "rm -rf dist docs", + "clean": "rm -rf dist", "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 --skipLibCheck", "watch": "rollup -wc", "lint": "markdownlint README.md && standard && tsc", - "serve-docs": "npm run docs && 0serve ./docs/", - "preversion": "PRODUCTION=1 npm run dist && npm run docs && test -e dist/src/index.d.ts && test -e dist/yjs.cjs && test -e dist/yjs.cjs", + "preversion": "PRODUCTION=1 npm run dist && 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", @@ -59,8 +58,7 @@ "standard": { "ignore": [ "/dist", - "/node_modules", - "/docs" + "/node_modules" ] }, "repository": { From d298ca9a7cf4e5b4a87ee0d62b71256a242c91cd Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 29 Oct 2025 02:51:54 +0100 Subject: [PATCH 360/362] 14.0.0-10 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b17f1e3c1..20d481661 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "yjs", - "version": "14.0.0-9", + "version": "14.0.0-10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "yjs", - "version": "14.0.0-9", + "version": "14.0.0-10", "license": "MIT", "dependencies": { "lib0": "^0.2.115-1" diff --git a/package.json b/package.json index 76c3eff84..ce048e6ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "14.0.0-9", + "version": "14.0.0-10", "description": "Shared Editing Library", "main": "./dist/yjs.cjs", "module": "./dist/yjs.mjs", From 2f7895e5ce3b90173a52c3f87528ac734ff1eb58 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Thu, 30 Oct 2025 03:26:24 +0100 Subject: [PATCH 361/362] fixes and more tests for delta representation on abstract types --- package-lock.json | 8 +- package.json | 2 +- src/structs/Item.js | 4 +- src/types/AbstractType.js | 25 ++++-- src/types/YArray.js | 12 --- src/types/YMap.js | 14 +--- src/types/YText.js | 4 - src/types/YXmlFragment.js | 11 --- src/utils/YEvent.js | 2 +- tests/delta.tests.js | 166 ++++++++++++++++++++++++++++++++++++++ tests/index.js | 3 +- 11 files changed, 197 insertions(+), 54 deletions(-) create mode 100644 tests/delta.tests.js diff --git a/package-lock.json b/package-lock.json index 20d481661..f09baa48c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "14.0.0-10", "license": "MIT", "dependencies": { - "lib0": "^0.2.115-1" + "lib0": "^0.2.115-2" }, "devDependencies": { "@types/node": "^22.14.1", @@ -3478,9 +3478,9 @@ } }, "node_modules/lib0": { - "version": "0.2.115-1", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.115-1.tgz", - "integrity": "sha512-mmQ4Pk/wZBsjdGMUJtXBhPsqPZof6Eh9sqrApA2Ufqe2eFYWW4yQPZWdf5/ak+dXsRlbslLHrGAn7+MeOY3TGA==", + "version": "0.2.115-2", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.115-2.tgz", + "integrity": "sha512-LBe5bPJTGG9/7F+1Ax1moAHrHJ1TaaTQWw7J2t6L19yHN3U6uHBSUcIRsews1f6J7fiKWwoiNohGCebd96lnig==", "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" diff --git a/package.json b/package.json index ce048e6ea..62feded07 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ }, "homepage": "https://docs.yjs.dev", "dependencies": { - "lib0": "^0.2.115-1" + "lib0": "^0.2.115-2" }, "devDependencies": { "@types/node": "^22.14.1", diff --git a/src/structs/Item.js b/src/structs/Item.js index e4070304a..137d328dc 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -282,7 +282,7 @@ export class Item extends AbstractStruct { * @param {ID | null} origin * @param {Item | null} right * @param {ID | null} rightOrigin - * @param {YType__|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it. + * @param {AbstractType|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it. * @param {string | null} parentSub * @param {AbstractContent} content */ @@ -309,7 +309,7 @@ export class Item extends AbstractStruct { */ this.rightOrigin = rightOrigin /** - * @type {YType__|ID|null} + * @type {AbstractType|ID|null} */ this.parent = parent /** diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 80da2b68c..d84119336 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -275,7 +275,7 @@ export const callTypeObservers = (type, transaction, event) => { /** * Abstract Yjs Type class - * @template {delta.Delta} [EventDelta=delta.Delta] + * @template {delta.Delta} [EventDelta=any] * @template {AbstractType} [Self=any] */ export class AbstractType { @@ -392,9 +392,11 @@ export class AbstractType { * Must be implemented by each type. * * @param {Transaction} transaction - * @param {Set} _parentSubs Keys changed on this type. `null` if list was modified. + * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. */ - _callObserver (transaction, _parentSubs) { + _callObserver (transaction, parentSubs) { + const event = new YEvent(/** @type {any} */ (this), transaction, parentSubs) + callTypeObservers(/** @type {any} */ (this), transaction, event) if (!transaction.local && this._searchMarker) { this._searchMarker.length = 0 } @@ -738,6 +740,19 @@ export class AbstractType { error.unexpectedCase() } } + for (const op of d.attrs) { + if (delta.$insertOp.check(op)) { + typeMapSet(transaction, /** @type {any} */ (this), op.key, op.value) + } else if (delta.$deleteOp.check(op)) { + typeMapDelete(transaction, /** @type {any} */ (this), op.key) + } else { + const sub = typeMapGet(/** @type {any} */ (this), op.key) + if (!(sub instanceof AbstractType)) { + error.unexpectedCase() + } + sub.applyDelta(op.value) + } + } }) } } @@ -1201,7 +1216,7 @@ export const typeMapDelete = (transaction, parent, key) => { /** * @param {Transaction} transaction - * @param {YType_} parent + * @param {AbstractType} parent * @param {string} key * @param {_YValue} value * @@ -1244,7 +1259,7 @@ export const typeMapSet = (transaction, parent, key, value) => { } /** - * @param {YType_} parent + * @param {AbstractType} parent * @param {string} key * @return {Object|number|null|Array|string|Uint8Array|AbstractType|undefined} * diff --git a/src/types/YArray.js b/src/types/YArray.js index 82d173664..39e29fd66 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -14,7 +14,6 @@ import { typeListDelete, typeListMap, YArrayRefID, - callTypeObservers, transact, warnPrematureAccess, typeListSlice, @@ -101,17 +100,6 @@ export class YArray extends AbstractType { return this._length } - /** - * Creates YArrayEvent and calls observers. - * - * @param {Transaction} transaction - * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. - */ - _callObserver (transaction, parentSubs) { - super._callObserver(transaction, parentSubs) - callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs)) - } - /** * Inserts new content at an index. * diff --git a/src/types/YMap.js b/src/types/YMap.js index f9ff292a4..b610c1df3 100644 --- a/src/types/YMap.js +++ b/src/types/YMap.js @@ -3,7 +3,6 @@ */ import { - YEvent, AbstractType, typeMapDelete, typeMapSet, @@ -11,10 +10,9 @@ import { typeMapHas, createMapIterator, YMapRefID, - callTypeObservers, transact, warnPrematureAccess, - UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line + UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line } from '../internals.js' import * as iterator from 'lib0/iterator' @@ -80,16 +78,6 @@ export class YMap extends AbstractType { return map } - /** - * Creates YMapEvent and calls observers. - * - * @param {Transaction} transaction - * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. - */ - _callObserver (transaction, parentSubs) { - callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs)) - } - /** * Transforms this Shared Type to a JSON object. * diff --git a/src/types/YText.js b/src/types/YText.js index dc50565fe..3a45cd87d 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -3,13 +3,11 @@ */ import { - YEvent, AbstractType, getItemCleanStart, getState, createID, YTextRefID, - callTypeObservers, transact, ContentEmbed, GC, @@ -705,8 +703,6 @@ export class YText extends AbstractType { */ _callObserver (transaction, parentSubs) { super._callObserver(transaction, parentSubs) - const event = new YEvent(/** @type {YText} */ (this), transaction, parentSubs) - callTypeObservers(/** @type {YText} */ (this), transaction, event) // If a remote change happened, we try to cleanup potential formatting duplicates. if (!transaction.local && this._hasFormatting) { transaction._needFormattingCleanup = true diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 14d5586bc..356f19349 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -12,7 +12,6 @@ import { typeListDelete, typeListToArray, YXmlFragmentRefID, - callTypeObservers, transact, typeListGet, typeListSlice, @@ -107,16 +106,6 @@ export class YXmlFragment extends AbstractType { return this._prelimContent === null ? this._length : this._prelimContent.length } - /** - * Creates YXmlEvent and calls observers. - * - * @param {Transaction} transaction - * @param {Set} parentSubs Keys changed on this type. `null` if list was modified. - */ - _callObserver (transaction, parentSubs) { - callTypeObservers(this, transaction, new YEvent(this, transaction, parentSubs)) - } - /** * Get the string representation of all the children of this YXmlFragment. * diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 5ee297c81..e209e80cc 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -12,7 +12,7 @@ import * as delta from 'lib0/delta' // eslint-disable-line */ /** - * @template {_YType} Target + * @template {AbstractType} Target * YEvent describes the changes on a YType. */ export class YEvent { diff --git a/tests/delta.tests.js b/tests/delta.tests.js new file mode 100644 index 000000000..252c56bb3 --- /dev/null +++ b/tests/delta.tests.js @@ -0,0 +1,166 @@ +import * as Y from '../src/index.js' +import * as delta from 'lib0/delta' +import * as t from 'lib0/testing' + +/** + * Delta is a versatyle format enabling you to efficiently describe changes. It is part of lib0, so + * that non-yjs applications can use it without consuming the full Yjs package. It is well suited + * for efficiently describing state & changesets. + * + * Assume we start with the text "hello world". Now we want to delete " world" and add an + * exclamation mark. The final content should be "hello!" ("hello world" => "hello!") + * + * In most editors, you would describe the necessary changes as replace operations using indexes. + * However, this might become ambiguous when many changes are involved. + * + * - delete range 5-11 + * - insert "!" at position 11 + * + * Using the delta format, you can describe the changes similar to what you would do in an text editor. + * The "|" describes the current cursor position. + * + * - d.retain(5) - "|hello world" => "hello| world" - jump over the next five characters + * - d.delete(6) - "hello| world" => "hello|" - delete the next 6 characres + * - d.insert('!') - "hello!|" - insert "!" at the current position + * => compact form: d.retain(5).delete(6).insert('!') + * + * You can also apply the changes in two distinct steps and then rebase the op so that you can apply + * them in two distinct steps. + * - delete " world": d1 = delta.create().retain(5).delete(6) + * - insert "!": d2 = delta.create().retain(11).insert('!') + * - rebase d2 on-top of d1: d2.rebase(d1) == delta.create().retain(5).insert('!') + * - merge into a single change: d1.apply(d2) == delta.create().retain(5).delete(6).insert(!) + * + * @param {t.TestCase} _tc + */ +export const testDeltaBasics = _tc => { + // the state of our text document + const state = delta.create().insert('hello world') + // describe changes: delete " world" & insert "!" + const change = delta.create().retain(5).delete(6).insert('!') + // apply changes to state + state.apply(change) + // compare state to expected state + t.assert(state.equals(delta.create().insert('hello!'))) +} + +/** + * Deltas can describe changes on attributes and children. Textual insertions are children. But we + * may also insert json-objects and other deltas as children. + * Key-value pairs can be represented as attributes. This "convoluted" changeset enables us to + * describe many changes in the same breath: + * + * delta.create().set('a', 42).retain(5).delete(6).insert('!').unset('b') + * + * @param {t.TestCase} _tc + */ +export const testDeltaValues = _tc => { + const change = delta.create().set('a', 42).unset('b').retain(5).delete(6).insert('!').insert([{ my: 'custom object' }]) + // iterate through attribute changes + for (const attrChange of change.attrs) { + if (delta.$insertOp.check(attrChange)) { + console.log(`set ${attrChange.key} to ${attrChange.value}`) + } else if (delta.$deleteOp.check(attrChange)) { + console.log(`delete ${attrChange.key}`) + } + } + // iterate through child changes + for (const childChange of change.children) { + if (delta.$retainOp.check(childChange)) { + console.log(`retain ${childChange.retain} child items`) + } else if (delta.$deleteOp.check(childChange)) { + console.log(`delete ${childChange.delete} child items`) + } else if (delta.$insertOp.check(childChange)) { + console.log(`insert child items:`, childChange.insert) + } else if (delta.$textOp.check(childChange)) { + console.log(`insert textual content`, childChange.insert) + } + } +} + +/** + * The new delta defines changes on attributes (key-value) and child elements (list & text), but can + * also be used to describe the current state of a document. + * + * 1. apply a delta to change a yjs type + * 2. observe deltas to read the differences + * 3. merge deltas to reflect multiple changes in a single delta + * 4. All Yjs types fully support the delta format. It is no longer necessary to define the type (such as Y.Array) + * + * @param {t.TestCase} _tc + */ +export const testBasics = _tc => { + const ydoc = new Y.Doc() + const ytype = ydoc.get('my data') + /** + * @type {delta.Delta} + */ + let observedDelta = delta.create() + ytype.observe(event => { + observedDelta = event.deltaDeep + console.log('ytype changed:', observedDelta.toJSON()) + }) + // define a change: set attribute: a=42 + const attrChange = delta.create().set('a', 42).done() + // define a change: insert textual content and an object + const childChange = delta.create().insert('hello').insert([{ my: 'object' }]).done() + // merge changes + const mergedChanges = delta.create(delta.$deltaAny).apply(attrChange).apply(childChange).done() + console.log('merged changes: ', mergedChanges.toJSON()) + ytype.applyDelta(mergedChanges) + // the observed change should equal the applied change + t.assert(observedDelta.equals(mergedChanges)) + // read the current state of the yjs types as a delta + const currState = ytype.getContentDeep() + t.assert(currState.equals(mergedChanges)) // equal to the changes that we applied +} + +/** + * Deltas allow us to describe the differences between two Yjs documents though "Attributions". + * + * - We can attribute changes to a user, or a group of users + * - There are 'insert', 'delete', and 'format' attributions + * - When we render attributions, we render inserted & deleted content as an insertions with special + * attributes which allow you to.. + * -- Render deleted content using a strikethrough: I.e. `hello w̶o̶r̶l̶d̶!` + * -- Render attributed insertions using a background color. + * + * @param {t.TestCase} _tc + */ +export const testAttributions = _tc => { + const ydocV1 = new Y.Doc() + const ytypeV1 = ydocV1.get('txt') + ytypeV1.applyDelta(delta.create().insert('hello world')) + // create a new version with updated content + const ydoc = new Y.Doc() + Y.applyUpdate(ydoc, Y.encodeStateAsUpdate(ydocV1)) + const ytype = ydoc.get('txt') + // delete " world" and insert exclamation mark "!". + ytype.applyDelta(delta.create().retain(5).delete(6).insert('!')) + const am = Y.createAttributionManagerFromDiff(ydocV1, ydoc) + // get the attributed differences + const attributedContent = ytype.getContent(am) + console.log('attributed content', attributedContent.toJSON()) + t.assert(attributedContent.equals(delta.create().insert('hello').insert(' world', null, { delete: [] }).insert('!', null, { insert: [] }))) + // for editor bindings, it is also necessary to observe changes and get the attributed changes + ytype.observe(event => { + const attributedChange = event.getDelta(am) + console.log('the attributed change', attributedChange.toJSON()) + t.assert(attributedChange.equals(delta.create().retain(11).insert('!', null, { insert: [] }))) + const unattributedChange = event.delta + console.log('the UNattributed change', unattributedChange.toJSON()) + t.assert(unattributedChange.equals(delta.create().retain(5).insert('!'))) + }) + /** + * Content now has different representations. + * - The UNattributed representation renders the latest state, without history. + * - The attributed representation renders the differences. + * + * Attributed: 'hello world!' + * UNattributed: 'world!' + */ + // Apply a change to the attributed content + ytype.applyDelta(delta.create().retain(11).insert('!'), am) + // // Equivalent to applying a change to the UNattributed content: + // ytype.applyDelta(delta.create().retain(5).insert('!')) +} diff --git a/tests/index.js b/tests/index.js index 9bd2894d0..83b536e04 100644 --- a/tests/index.js +++ b/tests/index.js @@ -14,6 +14,7 @@ import * as relativePositions from './relativePositions.tests.js' import * as idset from './IdSet.tests.js' import * as idmap from './IdMap.tests.js' import * as attribution from './attribution.tests.js' +import * as delta from './delta.tests.js' import { runTests } from 'lib0/testing' import { isBrowser, isNode } from 'lib0/environment' @@ -24,7 +25,7 @@ if (isBrowser) { } const tests = { - doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, idset, idmap, attribution + doc, map, array, text, xml, encoding, undoredo, compatibility, snapshot, updates, relativePositions, idset, idmap, attribution, delta } const run = async () => { From 19b513460458c108f630b8eced51b941ceaf841f Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Mon, 3 Nov 2025 18:13:34 +0100 Subject: [PATCH 362/362] fix all tests --- src/types/AbstractType.js | 3 +- src/types/YArray.js | 1 - src/types/YXmlFragment.js | 1 - tests/compatibility.tests.js | 2 +- tests/delta.tests.js | 35 ++++++++++-- tests/undo-redo.tests.js | 17 +++--- tests/y-text.tests.js | 8 +-- tests/y-xml.tests.js | 101 ++++++++++++----------------------- 8 files changed, 83 insertions(+), 85 deletions(-) diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index d84119336..1f4f04dd5 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -571,7 +571,7 @@ export class AbstractType { if (c.deleted ? retainDeletes : retainInserts) { d.retain(c.content.getLength(), null, attribution ?? {}) } else if (deep && c.content.constructor === ContentType) { - d.insert([/** @type {any} */(c.content).type.getContent(am, opts)], null, attribution) + d.insert([/** @type {any} */(c.content).type.getContent(am, { ...opts, renderChildren: true, renderAttrs: null })], null, attribution) } else { d.insert(c.content.getContent(), null, attribution) } @@ -579,6 +579,7 @@ export class AbstractType { d.delete(1) } else if (retainContent) { if (c.content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (c.content).type)) { + // @todo use current transaction instead d.modify(/** @type {any} */ (c.content).type.getContent(am, opts)) } else { d.usedAttributes = changedAttributes diff --git a/src/types/YArray.js b/src/types/YArray.js index 39e29fd66..eaad7db10 100644 --- a/src/types/YArray.js +++ b/src/types/YArray.js @@ -3,7 +3,6 @@ */ import { - YEvent, AbstractType, typeListGet, typeListToArray, diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 356f19349..be65a48ff 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -3,7 +3,6 @@ */ import { - YEvent, AbstractType, typeListMap, typeListForEach, diff --git a/tests/compatibility.tests.js b/tests/compatibility.tests.js index 98d97e92c..674d68628 100644 --- a/tests/compatibility.tests.js +++ b/tests/compatibility.tests.js @@ -38,7 +38,7 @@ export const testMapDecodingCompatibilityV1 = _tc => { export const testTextDecodingCompatibilityV1 = _tc => { const oldDoc = 'BS8EAAUBBHRleHRveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9RAQAATHBBAEEAAHBBAIEAAHEBAMEAAQxdXUKxQQCBANveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQMJBAlveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xgMBAwIGaXRhbGljBHRydWXGBAsDAgVjb2xvcgYiIzg4OCLEBAwDAgExxAQNAwIBMsEEDgMCAsYEEAMCBml0YWxpYwRudWxsxgQRAwIFY29sb3IEbnVsbMQDAQQLATHEBBMECwIyOcQEFQQLCzl6anpueXdvaHB4xAQgBAsIY25icmNhcQrBAxADEQHGAR8BIARib2xkBHRydWXGAgACAQRib2xkBG51bGzFAwkECm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GARABEQZpdGFsaWMEdHJ1ZcYELQERBWNvbG9yBiIjODg4IsYBEgETBml0YWxpYwRudWxsxgQvARMFY29sb3IEbnVsbMYCKwIsBGJvbGQEdHJ1ZcYCLQIuBGJvbGQEbnVsbMYCjAECjQEGaXRhbGljBHRydWXGAo4BAo8BBml0YWxpYwRudWxswQA2ADcBxgQ1ADcFY29sb3IGIiM4ODgixgNlA2YFY29sb3IEbnVsbMYDUwNUBGJvbGQEdHJ1ZcQEOANUFjEzMTZ6bHBrbWN0b3FvbWdmdGhicGfGBE4DVARib2xkBG51bGzGAk0CTgZpdGFsaWMEdHJ1ZcYEUAJOBWNvbG9yBiIjODg4IsYCTwJQBml0YWxpYwRudWxsxgRSAlAFY29sb3IEbnVsbMYChAEChQEGaXRhbGljBHRydWXGBFQChQEFY29sb3IGIiM4ODgixgKGAQKHAQZpdGFsaWMEbnVsbMYEVgKHAQVjb2xvcgRudWxsxAMpAyoRMTMyMWFwZ2l2eWRxc2pmc2XFBBIDAm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn0zAwAEAQR0ZXh0AjEyhAMBAzkwboQDBAF4gQMFAoQDBwJyCsQDBAMFBjEyOTd6bcQDDwMFAXbEAxADBQFwwQMRAwUBxAMSAwUFa3pxY2rEAxcDBQJzYcQDGQMFBHNqeQrBAxIDEwHBAAwAEAHEAA0ADgkxMzAyeGNpd2HEAygADgF5xAMpAA4KaGhlenVraXF0dMQDMwAOBWhudGsKxgMoAykEYm9sZAR0cnVlxAM5AykGMTMwNXJswQM/AykCxANBAykDZXlrxgNEAykEYm9sZARudWxsxAMzAzQJMTMwN3R2amllwQNOAzQCxANQAzQDamxoxANTAzQCZ3bEA1UDNAJsYsQDVwM0AmYKxgNBA0IEYm9sZARudWxswQNaA0ICxANcA0ICMDjBA14DQgLEA2ADQgEKxgNhA0IEYm9sZAR0cnVlxQIaAhtveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9wQA3ADgCwQNlADgBxANmADgKMTVteml3YWJ6a8EDcAA4AsQDcgA4BnJybXNjdsEDeAA4AcQCYgJjATHEA3oCYwIzMsQDfAJjCTRyb3J5d3RoccQDhQECYwEKxAOFAQOGARkxMzI1aW9kYnppenhobWxpYnZweXJ4bXEKwQN6A3sBxgOgAQN7BWNvbG9yBiIjODg4IsYDfAN9Bml0YWxpYwRudWxsxgOiAQN9BWNvbG9yBG51bGxSAgAEAQR0ZXh0ATGEAgACMjiEAgIBOYECAwKEAgUBdYQCBgJ0Y4QCCAJqZYECCgKEAgwBaoECDQGBAg4BhAIPAnVmhAIRAQrEAg4CDwgxMjkycXJtZsQCGgIPAmsKxgIGAgcGaXRhbGljBHRydWXGAggCCQZpdGFsaWMEbnVsbMYCEQISBml0YWxpYwR0cnVlxAIfAhIBMcECIAISAsQCIgISAzRoc8QCJQISAXrGAiYCEgZpdGFsaWMEbnVsbMEAFQAWAsQCKQAWATDEAioAFgEwxAIrABYCaHjEAi0AFglvamVldHJqaHjBAjYAFgLEAjgAFgJrcsQCOgAWAXHBAjsAFgHBAjwAFgHEAj0AFgFuxAI+ABYCZQrGAiUCJgZpdGFsaWMEbnVsbMQCQQImAjEzwQJDAiYCxAJFAiYIZGNjeGR5eGfEAk0CJgJ6Y8QCTwImA2Fwb8QCUgImAnRuxAJUAiYBcsQCVQImAmduwQJXAiYCxAJZAiYBCsYCWgImBml0YWxpYwR0cnVlxAI6AjsEMTMwM8QCXwI7A3VodsQCYgI7BmdhbmxuCsUCVQJWb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcECPAI9AcECPgI/AcYDFwMYBml0YWxpYwR0cnVlxgJsAxgFY29sb3IGIiM4ODgixgMZAxoGaXRhbGljBG51bGzGAm4DGgVjb2xvcgRudWxswQMQBCkBxAJwBCkKMTMwOXpsZ3ZqeMQCegQpAWfBAnsEKQLGBA0EDgZpdGFsaWMEbnVsbMYCfgQOBWNvbG9yBG51bGzEAn8EDgUxMzEwZ8QChAEEDgJ3c8QChgEEDgZoeHd5Y2jEAowBBA4Ca3HEAo4BBA4Ec2RydcQCkgEEDgRqcWljwQKWAQQOBMQCmgEEDgEKxgKbAQQOBml0YWxpYwR0cnVlxgKcAQQOBWNvbG9yBiIjODg4IsECaAI7AcQCCgEBFjEzMThqd3NramFiZG5kcmRsbWphZQrGA1UDVgRib2xkBHRydWXGA1cDWARib2xkBG51bGzGAEAAQQZpdGFsaWMEdHJ1ZcYCtwEAQQRib2xkBG51bGzEArgBAEESMTMyNnJwY3pucWFob3BjcnRkxgLKAQBBBml0YWxpYwRudWxsxgLLAQBBBGJvbGQEdHJ1ZRkBAMUCAgIDb3siaW1hZ2UiOiJodHRwczovL3VzZXItaW1hZ2VzLmdpdGh1YnVzZXJjb250ZW50LmNvbS81NTUzNzU3LzQ4OTc1MzA3LTYxZWZiMTAwLWYwNmQtMTFlOC05MTc3LWVlODk1ZTU5MTZlNS5wbmcifcQCCgILBzEyOTN0agrGABgAGQRib2xkBHRydWXGAA0ADgRib2xkBG51bGxEAgAHMTMwNnJ1cMQBEAIAAnVqxAESAgANaWtrY2pucmNwc2Nrd8QBHwIAAQrFBBMEFG97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3FAx0DBW97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAlICUwRib2xkBHRydWXGAlQCVQRib2xkBG51bGzGAnsCfAZpdGFsaWMEdHJ1ZcYBJQJ8BWNvbG9yBiIjODg4IsYBJgJ8BGJvbGQEbnVsbMQBJwJ8CjEzMTRweWNhdnXGATECfAZpdGFsaWMEbnVsbMYBMgJ8BWNvbG9yBG51bGzBATMCfAHFADEAMm97ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GADUANgZpdGFsaWMEdHJ1ZcEANwA4AcQAMgAzEzEzMjJybmJhb2tvcml4ZW52cArEAgUCBhcxMzIzbnVjdnhzcWx6bndsZmF2bXBjCsYDDwMQBGJvbGQEdHJ1ZR0AAMQEAwQEDTEyOTVxZnJ2bHlmYXDEAAwEBAFjxAANBAQCanbBAAwADQHEABAADQEywQARAA0ExAAVAA0DZHZmxAAYAA0BYcYCAwIEBml0YWxpYwR0cnVlwQAaAgQCxAAcAgQEMDRrdcYAIAIEBml0YWxpYwRudWxsxQQgBCFveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xQJAABZveyJpbWFnZSI6Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzU1NTM3NTcvNDg5NzUzMDctNjFlZmIxMDAtZjA2ZC0xMWU4LTkxNzctZWU4OTVlNTkxNmU1LnBuZyJ9xAQVBBYGMTMxMWtrxAIqAisIMTMxMnFyd3TEADECKwFixAAyAisDcnhxxAA1AisBasQANgIrAXjEADcCKwZkb3ZhbwrEAgAEKwMxMzHEAEAEKwkzYXhoa3RoaHXGAnoCewRib2xkBG51bGzFAEoCe297ImltYWdlIjoiaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vNTU1Mzc1Ny80ODk3NTMwNy02MWVmYjEwMC1mMDZkLTExZTgtOTE3Ny1lZTg5NWU1OTE2ZTUucG5nIn3GAEsCewRib2xkBHRydWXEAl8CYBExMzE3cGZjeWhrc3JrcGt0CsQBHwQqCzEzMTliY2Nna3AKxAKSAQKTARUxMzIwY29oYnZjcmtycGpuZ2RvYwoFBAQCAg8CKQE1AQADEAESBBsCAwsGAhIBHgJAAk8CWwJfAmQDcQJ5AaABAQIOBAILAg4CIQIoAjcCPAJEAlgCagJwAXwClwEEngEBAQI0ATcB' // eslint-disable-next-line - const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"291311kk9zjznywohpx"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"xr\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]}].map(x => ({ type: 'insert', ...x })) + const oldVal = [{"insert":"1306rup"},{"insert":"uj","format":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","format":{"bold":true}},{"insert":"1326rpcznqahopcrtd","format":{"italic":true}},{"insert":"3axhkthhu","format":{"bold":true}},{"insert":"28"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"9"},{"insert":"04ku","format":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","format":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"k\nuf"},{"insert":"14hs","format":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","format":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","format":{"bold":true}},{"insert":"r"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"gn\n"},{"insert":"z","format":{"italic":true}},{"insert":"\n121"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"291311kk9zjznywohpx"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"cnbrcaq\n"},{"insert":"1","format":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","format":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","format":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","format":{"italic":true,"color":"#888"}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","format":{"bold":true}},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"g","format":{"bold":true}},{"insert":"1314pycavu","format":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","format":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"xr\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"},{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}, {"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","format":{"italic":true}},{"insert":"x","format":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","format":{"italic":true}},{"insert":"hx","format":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","format":{"italic":true}},{"insert":"32","format":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]},{"insert":"dvf"},{"insert":"ac","format":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","format":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","format":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","format":{"bold":true}},{"insert":"gv"},{"insert":"lb","format":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":[{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}]}].map(x => ({ type: 'insert', ...x })) const doc = new Y.Doc() Y.applyUpdate(doc, buffer.fromBase64(oldDoc)) t.compare(doc.getText('text').getContent().toJSON().children, /** @type {any} */ (oldVal)) diff --git a/tests/delta.tests.js b/tests/delta.tests.js index 252c56bb3..7063df3a2 100644 --- a/tests/delta.tests.js +++ b/tests/delta.tests.js @@ -1,6 +1,7 @@ import * as Y from '../src/index.js' import * as delta from 'lib0/delta' import * as t from 'lib0/testing' +import * as s from 'lib0/schema' /** * Delta is a versatyle format enabling you to efficiently describe changes. It is part of lib0, so @@ -20,7 +21,7 @@ import * as t from 'lib0/testing' * The "|" describes the current cursor position. * * - d.retain(5) - "|hello world" => "hello| world" - jump over the next five characters - * - d.delete(6) - "hello| world" => "hello|" - delete the next 6 characres + * - d.delete(6) - "hello| world" => "hello|" - delete the next 6 characres * - d.insert('!') - "hello!|" - insert "!" at the current position * => compact form: d.retain(5).delete(6).insert('!') * @@ -44,6 +45,34 @@ export const testDeltaBasics = _tc => { t.assert(state.equals(delta.create().insert('hello!'))) } +/** + * lib0 also ships a schema library that can be used to validate JSON objects and custom data types, + * like Yjs types. + * + * As a convention, schemas are usually prefixed with a $ sign. This clarifies the difference + * between a schema, and an instance of a schema. + * + * const $myobj = s.$object({ key: s.$number }) + * let inputValue: any + * if ($myobj.check(inputValue)) { + * inputValue // is validated and of type $myobj + * } + * + * We can also define the expected values on a delta. + * + * @param {t.TestCase} _tc + */ +export const testDeltaBasicSchema = _tc => { + const $d = delta.$delta({ attrs: s.$object({ key: s.$string }), children: s.$number, hasText: false }) + const d = delta.create($d) + // @ts-expect-error + d.set('key', false) // invalid change: will throw a type error + t.fails(() => { + // @ts-expect-error + d.apply(delta.create().set('key', false)) // invalid delta: will throw a type error + }) +} + /** * Deltas can describe changes on attributes and children. Textual insertions are children. But we * may also insert json-objects and other deltas as children. @@ -71,9 +100,9 @@ export const testDeltaValues = _tc => { } else if (delta.$deleteOp.check(childChange)) { console.log(`delete ${childChange.delete} child items`) } else if (delta.$insertOp.check(childChange)) { - console.log(`insert child items:`, childChange.insert) + console.log('insert child items:', childChange.insert) } else if (delta.$textOp.check(childChange)) { - console.log(`insert textual content`, childChange.insert) + console.log('insert textual content', childChange.insert) } } } diff --git a/tests/undo-redo.tests.js b/tests/undo-redo.tests.js index 3304c34c4..c40c6985b 100644 --- a/tests/undo-redo.tests.js +++ b/tests/undo-redo.tests.js @@ -11,7 +11,7 @@ export const testInconsistentFormat = () => { const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText)) content.format(0, 6, { bold: null }) content.format(6, 4, { type: 'text' }) - t.compare(content.getContent(), delta.create().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true })) + t.compare(content.getContent(), delta.create().insert('Merge Test', { type: 'text' }).insert(' After', { type: 'text', italic: true }).done()) } const initializeYDoc = () => { const yDoc = new Y.Doc({ gc: false }) @@ -298,17 +298,18 @@ export const testUndoXml = tc => { t.assert(xml0.toString() === '

content

') // format textchild and revert that change undoManager.stopCapturing() - textchild.format(3, 4, { bold: {} }) - t.compare(xml0.getContentDeep(), delta.create('UNDEFINED').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done()) - t.assert(xml0.toString() === '

content

') + textchild.format(3, 4, { bold: true }) + const v1 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.text().insert('con').insert('tent', { bold: true }).done()]).done()]).done() + const v2 = delta.create('UNDEFINED').insert([delta.create('p').insert([delta.text().insert('content').done()]).done()]).done() + t.compare(xml0.getContentDeep(), v1) undoManager.undo() - t.assert(xml0.toString() === '

content

') + t.compare(xml0.getContentDeep(), v2) undoManager.redo() - t.assert(xml0.toString() === '

content

') + t.compare(xml0.getContentDeep(), v1) xml0.delete(0, 1) - t.assert(xml0.toString() === '') + t.compare(xml0.getContentDeep(), delta.create('UNDEFINED')) undoManager.undo() - t.assert(xml0.toString() === '

content

') + t.compare(xml0.getContentDeep(), v1) } /** diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index f846991e2..b84d67e8e 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -1439,18 +1439,18 @@ export const testTypesAsEmbed = tc => { text0.applyDelta(delta.create() .insert([new Y.Map([['key', 'val']])]) ) - t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ attrs: { key: { type: 'insert', value: 'val' } } }] }]) + t.compare(/** @type {any} */ (text0).getContentDeep().toJSON().children, [{ type: 'insert', insert: [{ type: 'delta', attrs: { key: { type: 'insert', value: 'val' } } }] }]) let firedEvent = false text1.observe(event => { const d = event.deltaDeep t.assert(d.children.len === 1) - t.compare(d.toJSON().children?.map(x => /** @type {any} */ (x).insert), [[{ key: { type: 'insert', value: 'val' } }]]) + t.compare(d.toJSON().children?.map(x => /** @type {any} */ (x).insert), [[{ type: 'delta', attrs: { key: { type: 'insert', value: 'val' } } }]]) firedEvent = true }) testConnector.flushAllMessages() - const dd = text1.getContent().toJSON().children + const dd = text1.getContentDeep().toJSON().children t.assert(dd?.length === 1) - t.compare(/** @type {any} */ (dd?.[0]).insert, { key: 'val' }) + t.compare(/** @type {any} */ (dd?.[0]).insert[0], { type: 'delta', attrs: { key: { type: 'insert', value: 'val' } } }) t.assert(firedEvent, 'fired the event observer containing a Type-Embed') } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index 038f77bc4..41e982a35 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -59,46 +59,6 @@ export const testHasProperty = tc => { compare(users) } -/** - * @param {t.TestCase} tc - */ -export const testEvents = tc => { - const { testConnector, users, xml0, xml1 } = init(tc, { users: 2 }) - /** - * @type {any} - */ - let event - /** - * @type {any} - */ - let remoteEvent - xml0.observe(e => { - event = e - }) - xml1.observe(e => { - remoteEvent = e - }) - xml0.setAttribute('key', 'value') - t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key') - testConnector.flushAllMessages() - t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key (remote)') - // check attributeRemoved - xml0.removeAttribute('key') - t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute') - testConnector.flushAllMessages() - t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute (remote)') - xml0.insert(0, [new Y.XmlText('some text')]) - t.assert(event.childListChanged, 'YXmlEvent.childListChanged on inserted element') - testConnector.flushAllMessages() - t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on inserted element (remote)') - // test childRemoved - xml0.delete(0) - t.assert(event.childListChanged, 'YXmlEvent.childListChanged on deleted element') - testConnector.flushAllMessages() - t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on deleted element (remote)') - compare(users) -} - /** * @param {t.TestCase} _tc */ @@ -252,28 +212,31 @@ export const testElementAttributedContent = _tc => { yelement.insert(1, [elem3]) yelement.setAttribute('key', '42') }) - const expectedContent = delta.create().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }) + const expectedContent = delta.create('UNDEFINED').insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] }).set('key', '42', { insert: [] }) const attributedContent = yelement.getContent(attributionManager) console.log('children', attributedContent.toJSON()) console.log('attributes', attributedContent) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { - const expectedContent = delta.create().insert( - [delta.text().insert('hello', null, { delete: [] })], - null, - { delete: [] } - ).insert([delta.create('span')]) + const expectedContent = delta.create('UNDEFINED') + .insert( + [delta.text().insert('hello', null, { delete: [] })], + null, + { delete: [] } + ) + .insert([delta.create('span')]) .insert([ delta.text().insert('world', null, { insert: [] }) ], null, { insert: [] }) + .set('key', '42', { insert: [] }) + .done() const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.toJSON().attrs) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent, delta.map().set('key', '42', { insert: [] })) - t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.name === 'UNDEFINED') }) }) @@ -296,28 +259,31 @@ export const testElementAttributedContentViaDiffer = _tc => { yelement.setAttribute('key', '42') }) const attributionManager = Y.createAttributionManagerFromDiff(ydocV1, ydoc) - const expectedContent = delta.create().insert([delta.create().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.create().insert('world', null, { insert: [] })], null, { insert: [] }) + const expectedContent = delta.create('UNDEFINED').insert([delta.create().insert('hello')], null, { delete: [] }).insert([elem2.getContentDeep()]).insert([delta.create().insert('world', null, { insert: [] })], null, { insert: [] }).set('key', '42', { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', attributedContent.toJSON().children) console.log('attributes', attributedContent.toJSON().attrs) t.compare(attributedContent.toJSON(), expectedContent.toJSON()) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } }) t.group('test getContentDeep', () => { - const expectedContent = delta.create().insert( - [delta.create().insert('hello')], - null, - { delete: [] } - ).insert([delta.create('span')]) + const expectedContent = delta.create('UNDEFINED') + .insert( + [delta.create().insert('hello')], + null, + { delete: [] } + ) + .insert([delta.create('span')]) .insert([ delta.create().insert('world', null, { insert: [] }) ], null, { insert: [] }) + .set('key', '42', { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.toJSON().attrs) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.name === 'UNDEFINED') }) ydoc.transact(() => { @@ -325,34 +291,37 @@ export const testElementAttributedContentViaDiffer = _tc => { }) t.group('test getContentDeep after some more updates', () => { t.info('expecting diffingAttributionManager to auto update itself') - const expectedContent = delta.create().insert( - [delta.create().insert('hello')], - null, - { delete: [] } - ).insert([delta.create('span')]) + const expectedContent = delta.create('UNDEFINED') + .insert( + [delta.create().insert('hello')], + null, + { delete: [] } + ) + .insert([delta.create('span')]) .insert([ delta.create().insert('bigworld', null, { insert: [] }) ], null, { insert: [] }) + .set('key', '42', { insert: [] }) const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.toJSON().attrs) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42', attribution: { insert: [] } } }) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42', attribution: { insert: [] } } }) t.assert(attributedContent.name === 'UNDEFINED') }) Y.applyUpdate(ydocV1, Y.encodeStateAsUpdate(ydoc)) t.group('test getContentDeep both docs synced', () => { t.info('expecting diffingAttributionManager to auto update itself') - const expectedContent = delta.create().insert([delta.create('span')]).insert([ + const expectedContent = delta.create('UNDEFINED').insert([delta.create('span')]).insert([ delta.create().insert('bigworld') - ]) + ]).set('key', '42') const attributedContent = yelement.getContentDeep(attributionManager) console.log('children', JSON.stringify(attributedContent.toJSON().children, null, 2)) console.log('cs expec', JSON.stringify(expectedContent.toJSON(), null, 2)) console.log('attributes', attributedContent.toJSON().attrs) t.assert(attributedContent.equals(expectedContent)) - t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', prevValue: undefined, value: '42' } }) + t.compare(attributedContent.toJSON().attrs, { key: { type: 'insert', value: '42' } }) t.assert(attributedContent.name === 'UNDEFINED') }) }