diff --git a/src/json-crdt-patch/codec/binary/Decoder.ts b/src/json-crdt-patch/codec/binary/Decoder.ts index 6e1e4a66f3..b07a78c3be 100644 --- a/src/json-crdt-patch/codec/binary/Decoder.ts +++ b/src/json-crdt-patch/codec/binary/Decoder.ts @@ -45,8 +45,8 @@ export class Decoder extends CborDecoder { protected decodeId(): ITimestampStruct { const reader = this.reader; - const [isRelativeTime, x] = reader.b1vu56(); - return isRelativeTime ? new Timestamp(this.patchSid!, x) : new Timestamp(x, reader.vu57()); + const [isSessionDifferent, x] = reader.b1vu56(); + return isSessionDifferent ? new Timestamp(reader.vu57(), x) : new Timestamp(this.patchSid!, x); } protected decodeTss(): ITimespanStruct { diff --git a/src/json-crdt-patch/codec/binary/Encoder.ts b/src/json-crdt-patch/codec/binary/Encoder.ts index 604d56c807..dc9bb5a7c7 100644 --- a/src/json-crdt-patch/codec/binary/Encoder.ts +++ b/src/json-crdt-patch/codec/binary/Encoder.ts @@ -52,10 +52,10 @@ export class Encoder extends CborEncoder { const time = id.time; const writer = this.writer; if (sessionId === this.patchSid) { - writer.b1vu56(1, time); + writer.b1vu56(0, time); } else { - writer.b1vu56(0, sessionId); - writer.vu57(time); + writer.b1vu56(1, time); + writer.vu57(sessionId); } } diff --git a/src/json-crdt/README.md b/src/json-crdt/README.md index 17724623ab..009ffdf084 100644 --- a/src/json-crdt/README.md +++ b/src/json-crdt/README.md @@ -13,11 +13,11 @@ and random positions of a 10,000-character newly created string. ``` node benchmarks/json-crdt/strings-short.js -json-crdt StringRga type x 228,880 ops/sec ±0.07% (99 runs sampled) +json-crdt StrNode type x 228,880 ops/sec ±0.07% (99 runs sampled) json-crdt x 187,700 ops/sec ±0.17% (98 runs sampled) Y.js x 18,288 ops/sec ±0.19% (100 runs sampled) Automerge x 23.64 ops/sec ±1.54% (44 runs sampled) -Fastest is json-crdt StringRga type +Fastest is json-crdt StrNode type ``` Inserting one 10-char string and deleting one 10-char string range at random @@ -25,11 +25,11 @@ positions from a 10,000-char string, which was beforehand already edited 5,000 t ``` node benchmarks/json-crdt/strings-long.js -json-crdt StringRga type x 54,250 ops/sec ±20.68% (23 runs sampled) +json-crdt StrNode type x 54,250 ops/sec ±20.68% (23 runs sampled) json-crdt x 59,628 ops/sec ±14.39% (32 runs sampled) Y.js x 16,358 ops/sec ±10.79% (45 runs sampled) Automerge x 1,777 ops/sec ±5.97% (76 runs sampled) -Fastest is json-crdt,json-crdt StringRga type +Fastest is json-crdt,json-crdt StrNode type ``` Editing a string at 10 repeating positions with +/- 6 characters random variance @@ -37,11 +37,11 @@ from those positions. ``` node benchmarks/json-crdt/strings-repeat-insert-positions.js -json-crdt StringRga type x 8,313 ops/sec ±1.52% (93 runs sampled) +json-crdt StrNode type x 8,313 ops/sec ±1.52% (93 runs sampled) json-crdt x 6,292 ops/sec ±2.22% (77 runs sampled) Y.js x 3,104 ops/sec ±1.40% (78 runs sampled) Automerge x 246 ops/sec ±1.04% (85 runs sampled) -Fastest is json-crdt StringRga type +Fastest is json-crdt StrNode type ``` ### Real-world comparisons @@ -114,22 +114,22 @@ String length: 104852 , Chunk count: 12487 JSON CRDT: 134.596ms String length: 104852 , Chunk count: 12487 --------------------------------------------- -JSON CRDT StringRga: 85.254ms +JSON CRDT StrNode: 85.254ms String length: 104852 , Chunk count: 12387 --------------------------------------------- -JSON CRDT StringRga: 86.487ms +JSON CRDT StrNode: 86.487ms String length: 104852 , Chunk count: 12387 --------------------------------------------- -JSON CRDT StringRga: 73.346ms +JSON CRDT StrNode: 73.346ms String length: 104852 , Chunk count: 12387 --------------------------------------------- -JSON CRDT StringRga: 74.109ms +JSON CRDT StrNode: 74.109ms String length: 104852 , Chunk count: 12387 --------------------------------------------- -JSON CRDT StringRga: 73.593ms +JSON CRDT StrNode: 73.593ms String length: 104852 , Chunk count: 12387 --------------------------------------------- -JSON CRDT StringRga: 74.138ms +JSON CRDT StrNode: 74.138ms String length: 104852 , Chunk count: 12387 --------------------------------------------- diamond-types-node: 58.114ms diff --git a/src/json-crdt/__bench__/bench.traces.crdt-libs.ts b/src/json-crdt/__bench__/bench.traces.crdt-libs.ts index c92e7a0658..fba99ac927 100644 --- a/src/json-crdt/__bench__/bench.traces.crdt-libs.ts +++ b/src/json-crdt/__bench__/bench.traces.crdt-libs.ts @@ -14,7 +14,7 @@ runTraceMatrix({ 'automerge-paper', ], editors: [ - 'StringRga (json-joy)', + 'StrNode (json-joy)', 'json-joy', // 'Y.js', // 'Y.rs', diff --git a/src/json-crdt/__bench__/bench.traces.non-crdt-libs.ts b/src/json-crdt/__bench__/bench.traces.non-crdt-libs.ts index 5b1126083a..b0a794fc8f 100644 --- a/src/json-crdt/__bench__/bench.traces.non-crdt-libs.ts +++ b/src/json-crdt/__bench__/bench.traces.non-crdt-libs.ts @@ -14,7 +14,7 @@ runTraceMatrix({ 'automerge-paper', ], editors: [ - 'StringRga (json-joy)', + 'StrNode (json-joy)', 'diamond-types-node', 'rope.js', 'V8 strings', diff --git a/src/json-crdt/__bench__/profiler/automerge-paper.js b/src/json-crdt/__bench__/profiler/automerge-paper.js index e582d15cc3..9e2f4d8b5d 100644 --- a/src/json-crdt/__bench__/profiler/automerge-paper.js +++ b/src/json-crdt/__bench__/profiler/automerge-paper.js @@ -4,18 +4,18 @@ */ const {traces} = require('../../data/editing-traces'); -const {StringRga} = require('../../../es2020/json-crdt/types/rga-string/StringRga'); +const {StrNode} = require('../../../es2020/json-crdt/types/str/StrNode'); const {ts} = require('../../../es2020/json-crdt-patch/clock/logical'); const patches = traces.get('automerge-paper').txns.map((txn) => txn.patches[0]); const length = patches.length; console.log('Document operations:', length, patches); -const runStringRga = () => { +const runStrNode = () => { console.log('---------------------------------------------'); - console.time('JSON CRDT StringRga'); + console.time('JSON CRDT StrNode'); let time = 0; - const rga = new StringRga(ts(1, time++)); + const rga = new StrNode(ts(1, time++)); for (let i = 0; i < length; i++) { const [pos, del, c] = patches[i]; if (del) { @@ -27,14 +27,14 @@ const runStringRga = () => { } rga.view(); // console.log(rga.view()); - console.timeEnd('JSON CRDT StringRga'); + console.timeEnd('JSON CRDT StrNode'); console.log('String length:', rga.length(), ', Chunk count:', rga.size()); // console.log(rga.toString()); }; -runStringRga(); -runStringRga(); -runStringRga(); -runStringRga(); -runStringRga(); -runStringRga(); +runStrNode(); +runStrNode(); +runStrNode(); +runStrNode(); +runStrNode(); +runStrNode(); diff --git a/src/json-crdt/__bench__/strings-long.js b/src/json-crdt/__bench__/strings-long.js index 4fddfeec71..fe67f3d855 100644 --- a/src/json-crdt/__bench__/strings-long.js +++ b/src/json-crdt/__bench__/strings-long.js @@ -1,6 +1,6 @@ const Benchmark = require('benchmark'); const {Model} = require('../../es2020/json-crdt'); -const {StringRga} = require('../../es2020/json-crdt/types/rga-string/StringRga'); +const {StrNode} = require('../../es2020/json-crdt/types/str/StrNode'); const Y = require('yjs'); const Automerge = require('automerge'); const {randomU32} = require('hyperdyperid/lib/randomU32'); @@ -14,10 +14,10 @@ const len2 = str2.length; const operations = 1; -const type = new StringRga(ts(1, 1)); +const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), str1); let time = str1.length + 100; -const editStringRga = () => { +const editStrNode = () => { for (let i = 0; i < operations; i++) { const pos1 = randomU32(0, len1 - len2); const pos2 = randomU32(0, len1 - len2); @@ -70,7 +70,7 @@ const editAutomerge = () => { }; for (let i = 0; i < 5000; i++) { - editStringRga(); + editStrNode(); editJsonCrdt(); editYjs(); editAutomerge(); @@ -79,8 +79,8 @@ for (let i = 0; i < 5000; i++) { const suite = new Benchmark.Suite(); suite - .add('json-crdt StringRga type', function () { - editStringRga(); + .add('json-crdt StrNode type', function () { + editStrNode(); }) .add('json-crdt', function () { editJsonCrdt(); diff --git a/src/json-crdt/__bench__/strings-repeat-insert-positions.js b/src/json-crdt/__bench__/strings-repeat-insert-positions.js index 1d1e756570..e1a0809665 100644 --- a/src/json-crdt/__bench__/strings-repeat-insert-positions.js +++ b/src/json-crdt/__bench__/strings-repeat-insert-positions.js @@ -1,6 +1,6 @@ const Benchmark = require('benchmark'); const {Model} = require('../../es2020/json-crdt'); -const {StringRga} = require('../../es2020/json-crdt/types/rga-string/StringRga'); +const {StrNode} = require('../../es2020/json-crdt/types/str/StrNode'); const Y = require('yjs'); const Automerge = require('automerge'); const {randomU32} = require('hyperdyperid/lib/randomU32'); @@ -15,10 +15,10 @@ const str2 = '1234567890'; const len1 = str1.length; const len2 = str2.length; -const type = new StringRga(ts(1, 1)); +const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), str1); let time = str1.length + 100; -const editStringRga = () => { +const editStrNode = () => { for (let i = 0; i < insertPositions.length; i++) { const pos1 = insertPositions[i] + randomU32(0, variance); const pos2 = randomU32(0, len1 - len2); @@ -71,7 +71,7 @@ const editAutomerge = () => { }; for (let i = 0; i < 1000; i++) { - editStringRga(); + editStrNode(); editJsonCrdt(); editYjs(); editAutomerge(); @@ -80,8 +80,8 @@ for (let i = 0; i < 1000; i++) { const suite = new Benchmark.Suite(); suite - .add('json-crdt StringRga type', function () { - editStringRga(); + .add('json-crdt StrNode type', function () { + editStrNode(); }) .add('json-crdt', function () { editJsonCrdt(); diff --git a/src/json-crdt/__bench__/strings-short.js b/src/json-crdt/__bench__/strings-short.js index 2b782558ab..cd3fbea4ef 100644 --- a/src/json-crdt/__bench__/strings-short.js +++ b/src/json-crdt/__bench__/strings-short.js @@ -1,6 +1,6 @@ const Benchmark = require('benchmark'); const {Model} = require('../../es2020/json-crdt'); -const {StringRga} = require('../../es2020/json-crdt/types/rga-string/StringRga'); +const {StrNode} = require('../../es2020/json-crdt/types/str/StrNode'); const Y = require('yjs'); const Automerge = require('automerge'); const {randomU32} = require('hyperdyperid/lib/randomU32'); @@ -14,8 +14,8 @@ const str2 = '1234567890'; const len1 = str1.length; const len2 = str2.length; -const editStringRga = () => { - const type = new StringRga(ts(1, 1)); +const editStrNode = () => { + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), str1); let time = str1.length + 100; for (let i = 0; i < 10; i++) { @@ -71,7 +71,7 @@ const editAutomerge = () => { }; for (let i = 0; i < 10; i++) { - editStringRga(); + editStrNode(); editJsonCrdt(); editYjs(); editAutomerge(); @@ -86,8 +86,8 @@ suite // arr.splice(pos1, {i: null, r: null, p: null, content: {}, deleted: false, id: [1 ,2]}, {i: null, r: null, p: null, content: {}, deleted: false, id: [1 ,2]}); // arr.splice(pos2, 2); // }) - .add('json-crdt StringRga type', function () { - editStringRga(); + .add('json-crdt StrNode type', function () { + editStrNode(); }) .add('json-crdt', function () { editJsonCrdt(); diff --git a/src/json-crdt/__bench__/util/editors.ts b/src/json-crdt/__bench__/util/editors.ts index 3e3403275a..84e8a41c2b 100644 --- a/src/json-crdt/__bench__/util/editors.ts +++ b/src/json-crdt/__bench__/util/editors.ts @@ -1,5 +1,5 @@ import {Timestamp} from '../../../json-crdt-patch/clock'; -import {StringRga} from '../../types/rga-string/StringRga'; +import {StrNode} from '../../nodes'; import {Model} from '../../model'; import {Doc} from 'diamond-types-node'; import * as Y from 'yjs'; @@ -11,11 +11,11 @@ const AutomergeUnstable = require('@automerge/automerge/dist/cjs/unstable'); const Rope = require('rope.js'); export const editors = { - 'StringRga (json-joy)': { - name: 'StringRga (json-joy)', + 'StrNode (json-joy)': { + name: 'StrNode (json-joy)', factory: () => { let time = 0; - const rga = new StringRga(new Timestamp(1, time++)); + const rga = new StrNode(new Timestamp(1, time++)); return { ins: (pos: number, insert: string) => { rga.insAt(pos, new Timestamp(1, time), insert); diff --git a/src/json-crdt/__demos__/StringRga-hello-world.ts b/src/json-crdt/__demos__/StrNode-hello-world.ts similarity index 74% rename from src/json-crdt/__demos__/StringRga-hello-world.ts rename to src/json-crdt/__demos__/StrNode-hello-world.ts index 65d673438c..0c50c268a2 100644 --- a/src/json-crdt/__demos__/StringRga-hello-world.ts +++ b/src/json-crdt/__demos__/StrNode-hello-world.ts @@ -3,17 +3,17 @@ /** * Run this demo with: * - * npx nodemon -q -x ts-node src/json-crdt/__demos__/StringRga-hello-world.ts + * npx nodemon -q -x ts-node src/json-crdt/__demos__/StrNode-hello-world.ts */ import {ts} from '../../json-crdt-patch/clock'; -import {StringRga} from '../types/rga-string/StringRga'; +import {StrNode} from '../nodes'; const sid = 123; // Site ID let time = 0; // "time" const id = ts(sid, time++); -const str = new StringRga(id); +const str = new StrNode(id); console.log(str.view()); console.log(str + ''); diff --git a/src/json-crdt/__demos__/StringRga-json-joy.ts b/src/json-crdt/__demos__/StrNode-json-joy.ts similarity index 55% rename from src/json-crdt/__demos__/StringRga-json-joy.ts rename to src/json-crdt/__demos__/StrNode-json-joy.ts index 94a91866e0..5734b15b73 100644 --- a/src/json-crdt/__demos__/StringRga-json-joy.ts +++ b/src/json-crdt/__demos__/StrNode-json-joy.ts @@ -3,11 +3,11 @@ /** * Run this demo with: * - * npx nodemon -q -x ts-node src/json-crdt/__demos__/StringRga-json-joy.ts + * npx nodemon -q -x ts-node src/json-crdt/__demos__/StrNode-json-joy.ts */ import {ts} from '../../json-crdt-patch/clock'; -import {StringRga} from '../types/rga-string/StringRga'; +import {StrNode} from '../nodes'; const user1 = 123; const user2 = 345; @@ -16,7 +16,7 @@ const user3 = 789; console.log(); // User 1 creates a new string -const str1 = new StringRga(ts(user1, 0)); +const str1 = new StrNode(ts(user1, 0)); str1.insAt(0, ts(user1, 1), 'js'); // User 2 and 3 insert their changes at the same time "ts(user1, 2)" @@ -24,15 +24,15 @@ str1.ins(ts(user1, 2), ts(user2, 3), ' joy'); str1.ins(ts(user1, 2), ts(user3, 3), 'on'); console.log(str1 + ''); -// StringRga 123.0 { "json joy" } -// └─ StringChunk 789.3!2 len:8 { "on" } -// ← StringChunk 123.1!2 len:2 { "js" } -// → StringChunk 345.3!4 len:4 { " joy" } +// StrNode 123.0 { "json joy" } +// └─ StrChunk 789.3!2 len:8 { "on" } +// ← StrChunk 123.1!2 len:2 { "js" } +// → StrChunk 345.3!4 len:4 { " joy" } console.log(); // User 1 creates a new string "js" -const str2 = new StringRga(ts(user1, 0)); +const str2 = new StrNode(ts(user1, 0)); str2.insAt(0, ts(user1, 1), 'js'); // User 2 and 3 insert their changes at the same time "ts(user1, 2)" @@ -40,7 +40,7 @@ str2.ins(ts(user1, 2), ts(user3, 3), 'on'); str2.ins(ts(user1, 2), ts(user2, 3), ' joy'); console.log(str2 + ''); -// StringRga 123.0 { "json joy" } -// └─ StringChunk 345.3!4 len:8 { " joy" } -// ← StringChunk 789.3!2 len:4 { "on" } -// ← StringChunk 123.1!2 len:2 { "js" } +// StrNode 123.0 { "json joy" } +// └─ StrChunk 345.3!4 len:8 { " joy" } +// ← StrChunk 789.3!2 len:4 { "on" } +// ← StrChunk 123.1!2 len:2 { "js" } diff --git a/src/json-crdt/__demos__/docs-arr.ts b/src/json-crdt/__demos__/docs-arr.ts index 28046e223e..4ef6662fdf 100644 --- a/src/json-crdt/__demos__/docs-arr.ts +++ b/src/json-crdt/__demos__/docs-arr.ts @@ -21,17 +21,17 @@ console.log(model.view()); // { tags: [ 'big', 'small', 'red' ] } console.log(model.root + ''); -// RootLww "val" 0.0 -// └─ ObjectLww "obj" 1234.1 +// RootNode 0.0 +// └─ ObjNode 1234.1 // └─ "tags" -// └─ ArrayRga "arr" 1234.2 -// └─ ArrayChunk 1234.17!3 len:3 -// ├─ [0]: StringRga "str" 1234.3 { "big" } -// │ └─ StringChunk 1234.4!3 len:3 { "big" } -// ├─ [1]: StringRga "str" 1234.7 { "small" } -// │ └─ StringChunk 1234.8!5 len:5 { "small" } -// └─ [2]: StringRga "str" 1234.13 { "red" } -// └─ StringChunk 1234.14!3 len:3 { "red" } +// └─ ArrNode 1234.2 +// └─ ArrChunk 1234.17!3 len:3 +// ├─ [0]: StrNode 1234.3 { "big" } +// │ └─ StrChunk 1234.4!3 len:3 { "big" } +// ├─ [1]: StrNode 1234.7 { "small" } +// │ └─ StrChunk 1234.8!5 len:5 { "small" } +// └─ [2]: StrNode 1234.13 { "red" } +// └─ StrChunk 1234.14!3 len:3 { "red" } model.api.obj([]).set({ tags: [konst('big'), konst('small'), konst('red')], @@ -41,53 +41,53 @@ console.log(model.view()); // { tags: [ 'big', 'small', 'red' ] } console.log(model.root + ''); -// RootLww "val" 0.0 -// └─ ObjectLww "obj" 1234.1 +// RootNode 0.0 +// └─ ObjNode 1234.1 // └─ "tags" -// └─ ArrayRga "arr" 1234.22 -// └─ ArrayChunk 1234.26!3 len:3 -// ├─ [0]: Const "con" 1234.23 { "big" } -// ├─ [1]: Const "con" 1234.24 { "small" } -// └─ [2]: Const "con" 1234.25 { "red" } +// └─ ArrNode 1234.22 +// └─ ArrChunk 1234.26!3 len:3 +// ├─ [0]: ConNode 1234.23 { "big" } +// ├─ [1]: ConNode 1234.24 { "small" } +// └─ [2]: ConNode 1234.25 { "red" } // Retrieve node at path ['tags'] as "arr" type. const tags = model.api.arr(['tags']); console.log(tags + ''); -// ArrayApi -// └─ ArrayRga "arr" 1234.22 -// └─ ArrayChunk 1234.26!3 len:3 -// ├─ [0]: Const "con" 1234.23 { "big" } -// ├─ [1]: Const "con" 1234.24 { "small" } -// └─ [2]: Const "con" 1234.25 { "red" } +// ArrApi +// └─ ObjNode 1234.22 +// └─ ArrChunk 1234.26!3 len:3 +// ├─ [0]: ConNode 1234.23 { "big" } +// ├─ [1]: ConNode 1234.24 { "small" } +// └─ [2]: ConNode 1234.25 { "red" } tags.ins(1, [konst('medium'), konst('blue')]); console.log(tags + ''); -// ArrayApi -// └─ ArrayRga "arr" 1234.22 -// └─ ArrayChunk 1234.32!2 len:5 -// ├─ [1]: Const "con" 1234.30 { "medium" } -// └─ [2]: Const "con" 1234.31 { "blue" } -// ← ArrayChunk 1234.26!1 len:1 -// └─ [0]: Const "con" 1234.23 { "big" } -// → ArrayChunk 1234.27!2 len:2 -// ├─ [3]: Const "con" 1234.24 { "small" } -// └─ [4]: Const "con" 1234.25 { "red" } +// ArrApi +// └─ ArrNode 1234.22 +// └─ ArrChunk 1234.32!2 len:5 +// ├─ [1]: ConNode 1234.30 { "medium" } +// └─ [2]: ConNode 1234.31 { "blue" } +// ← ArrChunk 1234.26!1 len:1 +// └─ [0]: ConNode 1234.23 { "big" } +// → ArrChunk 1234.27!2 len:2 +// ├─ [3]: ConNode 1234.24 { "small" } +// └─ [4]: ConNode 1234.25 { "red" } console.log(tags.view()); // [ 'big', 'medium', 'blue', 'small', 'red' ] tags.del(2, 2); console.log(tags + ''); -// ArrayApi -// └─ ArrayRga "arr" 1234.22 -// └─ ArrayChunk 1234.32!1 len:3 -// └─ [1]: Const "con" 1234.30 { "medium" } -// ← ArrayChunk 1234.26!1 len:1 -// └─ [0]: Const "con" 1234.23 { "big" } -// → ArrayChunk 1234.33!1 len:1 [1] -// → ArrayChunk 1234.27!1 len:1 [1] -// → ArrayChunk 1234.28!1 len:1 -// └─ [2]: Const "con" 1234.25 { "red" } +// ArrApi +// └─ ArrNode 1234.22 +// └─ ArrChunk 1234.32!1 len:3 +// └─ [1]: ConNode 1234.30 { "medium" } +// ← ArrChunk 1234.26!1 len:1 +// └─ [0]: ConNode 1234.23 { "big" } +// → ArrChunk 1234.33!1 len:1 [1] +// → ArrChunk 1234.27!1 len:1 [1] +// → ArrChunk 1234.28!1 len:1 +// └─ [2]: ConNode 1234.25 { "red" } console.log(model.view()); // { tags: [ 'big', 'medium', 'red' ] } diff --git a/src/json-crdt/__demos__/docs-bin.ts b/src/json-crdt/__demos__/docs-bin.ts index 78d630762e..a9fc552476 100644 --- a/src/json-crdt/__demos__/docs-bin.ts +++ b/src/json-crdt/__demos__/docs-bin.ts @@ -20,33 +20,33 @@ console.log(model.view()); // { blob: Uint8Array(3) [ 1, 2, 3 ] } console.log(model.root + ''); -// RootLww "val" 0.0 -// └─ ObjectLww "obj" 1234.1 +// RootNode 0.0 +// └─ ObjNode 1234.1 // └─ "blob" -// └─ BinaryRga "bin" 1234.2 { 1, 2, 3 } -// └─ BinaryChunk 1234.3!3 len:3 { 1, 2, 3 } +// └─ BinNode 1234.2 { 1, 2, 3 } +// └─ BinChunk 1234.3!3 len:3 { 1, 2, 3 } // Retrieve node at path ['blob'] as "bin" type. const blob = model.api.bin(['blob']); console.log(blob + ''); -// BinaryApi -// └─ BinaryRga "bin" 1234.2 { 1, 2, 3 } -// └─ BinaryChunk 1234.3!3 len:3 { 1, 2, 3 } +// BinApi +// └─ BinNode 1234.2 { 1, 2, 3 } +// └─ BinChunk 1234.3!3 len:3 { 1, 2, 3 } blob.ins(3, new Uint8Array([4, 5])); console.log(blob + ''); -// BinaryApi -// └─ BinaryRga "bin" 1234.2 { 1, 2, 3, 4, 5 } -// └─ BinaryChunk 1234.8!2 len:5 { 4, 5 } -// ← BinaryChunk 1234.3!3 len:3 { 1, 2, 3 } +// BinApi +// └─ BinNode 1234.2 { 1, 2, 3, 4, 5 } +// └─ BinChunk 1234.8!2 len:5 { 4, 5 } +// ← BinChunk 1234.3!3 len:3 { 1, 2, 3 } blob.del(2, 1); console.log(blob + ''); -// BinaryApi -// └─ BinaryRga "bin" 1234.2 { 1, 2, 4, 5 } -// └─ BinaryChunk 1234.8!2 len:4 { 4, 5 } -// ← BinaryChunk 1234.3!2 len:2 { 1, 2 } -// → BinaryChunk 1234.5!1 len:0 [1] +// BinApi +// └─ BinNode 1234.2 { 1, 2, 4, 5 } +// └─ BinChunk 1234.8!2 len:4 { 4, 5 } +// ← BinChunk 1234.3!2 len:2 { 1, 2 } +// → BinChunk 1234.5!1 len:0 [1] console.log(model.view()); // { blob: Uint8Array(4) [ 1, 2, 4, 5 ] } diff --git a/src/json-crdt/__demos__/docs-obj.ts b/src/json-crdt/__demos__/docs-obj.ts index 9686601492..755d8162c5 100644 --- a/src/json-crdt/__demos__/docs-obj.ts +++ b/src/json-crdt/__demos__/docs-obj.ts @@ -20,16 +20,16 @@ model.api.root({ }); console.log(model.root + ''); -// RootLww "val" 0.0 -// └─ ObjectLww "obj" 1234.1 +// RootNode 0.0 +// └─ ObjNode 1234.1 // └─ "foo" -// └─ ObjectLww "obj" 1234.2 +// └─ ObjNode 1234.2 // └─ "bar" -// └─ ObjectLww "obj" 1234.3 +// └─ ObjNode 1234.3 // ├─ "x" -// │ └─ Const "con" 1234.4 { 1 } +// │ └─ ConNode 1234.4 { 1 } // └─ "y" -// └─ Const "con" 1234.5 { 2 } +// └─ ConNode 1234.5 { 2 } console.log(model.view()); // { foo: { bar: { x: 1, y: 2 } } } @@ -38,11 +38,11 @@ console.log(model.view()); const bar = model.api.obj(['foo', 'bar']); console.log(bar + ''); // ObjectApi -// └─ ObjectLww "obj" 1234.3 +// └─ ObjNode 1234.3 // ├─ "x" -// │ └─ Const "con" 1234.4 { 1 } +// │ └─ ConNode 1234.4 { 1 } // └─ "y" -// └─ Const "con" 1234.5 { 2 } +// └─ ConNode 1234.5 { 2 } bar.set({ x: 24, @@ -50,13 +50,13 @@ bar.set({ }); console.log(bar + ''); // ObjectApi -// └─ ObjectLww "obj" 1234.3 +// └─ ObjNode 1234.3 // ├─ "x" -// │ └─ Const "con" 1234.10 { 24 } +// │ └─ ConNode 1234.10 { 24 } // ├─ "y" -// │ └─ Const "con" 1234.5 { 2 } +// │ └─ ConNode 1234.5 { 2 } // └─ "z" -// └─ Const "con" 1234.11 { 42 } +// └─ ConNode 1234.11 { 42 } console.log(bar.view()); // { x: 24, y: 2, z: 42 } @@ -65,13 +65,13 @@ bar.del(['y']); console.log(bar + ''); // ObjectApi -// └─ ObjectLww "obj" 1234.3 +// └─ ObjNode 1234.3 // ├─ "x" -// │ └─ Const "con" 1234.10 { 24 } +// │ └─ ConNode 1234.10 { 24 } // ├─ "y" -// │ └─ Const "con" 1234.13 { undefined } +// │ └─ ConNode 1234.13 { undefined } // └─ "z" -// └─ Const "con" 1234.11 { 42 } +// └─ ConNode 1234.11 { 42 } console.log(bar.view()); // { x: 24, z: 42 } diff --git a/src/json-crdt/__demos__/docs-str.ts b/src/json-crdt/__demos__/docs-str.ts index e93b0a7182..de9ef873ea 100644 --- a/src/json-crdt/__demos__/docs-str.ts +++ b/src/json-crdt/__demos__/docs-str.ts @@ -15,11 +15,11 @@ model.api.root({ }); console.log(model.root + ''); -// RootLww "val" 0.0 -// └─ ObjectLww "obj" 1234.1 +// RootNode 0.0 +// └─ ObjNode 1234.1 // └─ "text" -// └─ StringRga "str" 1234.2 { "hello" } -// └─ StringChunk 1234.3!5 len:5 { "hello" } +// └─ StrNode 1234.2 { "hello" } +// └─ StrChunk 1234.3!5 len:5 { "hello" } console.log(model.view()); // { text: 'hello' } @@ -27,24 +27,24 @@ console.log(model.view()); // Retrieve node at path ['text'] as "str" type. const text = model.api.str(['text']); console.log(text + ''); -// StringApi -// └─ StringRga "str" 1234.2 { "hello" } -// └─ StringChunk 1234.3!5 len:5 { "hello" } +// StrApi +// └─ StrNode 1234.2 { "hello" } +// └─ StrChunk 1234.3!5 len:5 { "hello" } text.ins(5, ' world'); console.log(text + ''); -// StringApi -// └─ StringRga "str" 1234.2 { "hello world" } -// └─ StringChunk 1234.10!6 len:11 { " world" } -// ← StringChunk 1234.3!5 len:5 { "hello" } +// StrApi +// └─ StrNode 1234.2 { "hello world" } +// └─ StrChunk 1234.10!6 len:11 { " world" } +// ← StrChunk 1234.3!5 len:5 { "hello" } text.del(0, 6); console.log(text + ''); -// StringApi -// └─ StringRga "str" 1234.2 { "world" } -// └─ StringChunk 1234.10!1 len:5 [1] -// ← StringChunk 1234.3!5 len:0 [5] -// → StringChunk 1234.11!5 len:5 { "world" } +// StrApi +// └─ StrNode 1234.2 { "world" } +// └─ StrChunk 1234.10!1 len:5 [1] +// ← StrChunk 1234.3!5 len:0 [5] +// → StrChunk 1234.11!5 len:5 { "world" } console.log(model.view()); // { text: 'world' } diff --git a/src/json-crdt/__demos__/docs-vec.ts b/src/json-crdt/__demos__/docs-vec.ts index fa6aff73cd..a3b7fffde4 100644 --- a/src/json-crdt/__demos__/docs-vec.ts +++ b/src/json-crdt/__demos__/docs-vec.ts @@ -18,14 +18,14 @@ model.api.root({ }); console.log(model.root + ''); -// RootLww "val" 0.0 -// └─ ObjectLww "obj" 1234.1 +// RootNode 0.0 +// └─ ObjNode 1234.1 // └─ "foo" -// └─ ObjectLww "obj" 1234.2 +// └─ ObjNode 1234.2 // └─ "bar" -// └─ ArrayLww "vec" 1234.3 -// ├─ 0: Const "con" 1234.4 { 1 } -// └─ 1: Const "con" 1234.5 { 2 } +// └─ VecNode 1234.3 +// ├─ 0: ConNode 1234.4 { 1 } +// └─ 1: ConNode 1234.5 { 2 } console.log(model.view()); // { foo: { bar: [ 1, 2 ] } } @@ -33,21 +33,21 @@ console.log(model.view()); // Retrieve node at path ['foo', 'bar'] as "vec" type. const bar = model.api.vec(['foo', 'bar']); console.log(bar + ''); -// VectorApi -// └─ ArrayLww "vec" 1234.3 -// ├─ 0: Const "con" 1234.4 { 1 } -// └─ 1: Const "con" 1234.5 { 2 } +// VecApi +// └─ VecNode 1234.3 +// ├─ 0: ConNode 1234.4 { 1 } +// └─ 1: ConNode 1234.5 { 2 } bar.set([ [0, 24], [2, 42], ]); console.log(bar + ''); -// VectorApi -// └─ ArrayLww "vec" 1234.3 -// ├─ 0: Const "con" 1234.10 { 24 } -// ├─ 1: Const "con" 1234.5 { 2 } -// └─ 2: Const "con" 1234.11 { 42 } +// VecApi +// └─ VecNode 1234.3 +// ├─ 0: ConNode 1234.10 { 24 } +// ├─ 1: ConNode 1234.5 { 2 } +// └─ 2: ConNode 1234.11 { 42 } console.log(bar.view()); // [ 24, 2, 42 ] diff --git a/src/json-crdt/__demos__/getting-started.ts b/src/json-crdt/__demos__/getting-started.ts index 27bb4d1807..5f57e081a1 100644 --- a/src/json-crdt/__demos__/getting-started.ts +++ b/src/json-crdt/__demos__/getting-started.ts @@ -55,14 +55,14 @@ console.log(blob); const fork = Model.fromBinary(blob); console.log(fork + ''); // Model -// ├─ RootLww "val" 0.0 -// │ └─ ObjectLww "obj" 1234.1 +// ├─ RootNode 0.0 +// │ └─ ObjNode 1234.1 // │ ├─ "counter" -// │ │ └─ Const "con" 1234.11 { 25 } +// │ │ └─ ConNode 1234.11 { 25 } // │ └─ "text" -// │ └─ StringRga "str" 1234.3 { "Hello world!" } -// │ └─ StringChunk 1234.13!7 len:12 { " world!" } -// │ ← StringChunk 1234.4!5 len:5 { "Hello" } +// │ └─ StrNode 1234.3 { "Hello world!" } +// │ └─ StrChunk 1234.13!7 len:12 { " world!" } +// │ ← StrChunk 1234.4!5 len:5 { "Hello" } // │ // └─ VectorClock 1234.20 diff --git a/src/json-crdt/__demos__/schema.ts b/src/json-crdt/__demos__/schema.ts index 9046c68cab..4f3cb8d5df 100644 --- a/src/json-crdt/__demos__/schema.ts +++ b/src/json-crdt/__demos__/schema.ts @@ -24,25 +24,25 @@ const model = Model.withLogicalClock(1234).setSchema(schema); console.log(model + ''); // Model -// ├─ RootLww "val" 0.0 -// │ └─ ObjectLww "obj" 1234.1 +// ├─ RootNode 0.0 +// │ └─ ObjNode 1234.1 // │ ├─ "text" -// │ │ └─ Const "con" 1234.2 { "hello" } +// │ │ └─ ConNode 1234.2 { "hello" } // │ ├─ "counter" -// │ │ └─ Const "con" 1234.3 { 0 } +// │ │ └─ ConNode 1234.3 { 0 } // │ ├─ "checked" -// │ │ └─ Const "con" 1234.4 { true } +// │ │ └─ ConNode 1234.4 { true } // │ └─ "friend" -// │ └─ ObjectLww "obj" 1234.5 +// │ └─ ObjNode 1234.5 // │ ├─ "name" -// │ │ └─ Const "con" 1234.6 { "John" } +// │ │ └─ ConNode 1234.6 { "John" } // │ ├─ "age" -// │ │ └─ Const "con" 1234.7 { 42 } +// │ │ └─ ConNode 1234.7 { 42 } // │ └─ "tags" -// │ └─ ArrayRga "arr" 1234.8 -// │ └─ ArrayChunk 1234.11!2 len:2 -// │ ├─ [0]: Const "con" 1234.9 { "foo" } -// │ └─ [1]: Const "con" 1234.10 { "bar" } +// │ └─ ArrNode 1234.8 +// │ └─ ArrChunk 1234.11!2 len:2 +// │ ├─ [0]: ConNode 1234.9 { "foo" } +// │ └─ [1]: ConNode 1234.10 { "bar" } // │ // └─ VectorClock 1234.16 diff --git a/src/json-crdt/__demos__/type-safety.ts b/src/json-crdt/__demos__/type-safety.ts index 96c9a4f879..689196cd8a 100644 --- a/src/json-crdt/__demos__/type-safety.ts +++ b/src/json-crdt/__demos__/type-safety.ts @@ -32,20 +32,20 @@ console.log(view); console.log(model + ''); // Model -// ├─ RootLww "val" 0.0 -// │ └─ ObjectLww "obj" 1234.1 +// ├─ RootNode 0.0 +// │ └─ ObjNode 1234.1 // │ ├─ "num" -// │ │ └─ Const "con" 1234.2 { 123 } +// │ │ └─ ConNode 1234.2 { 123 } // │ ├─ "text" -// │ │ └─ StringRga "str" 1234.3 { "hello" } -// │ │ └─ StringChunk 1234.4!5 len:5 { "hello" } +// │ │ └─ StrNode 1234.3 { "hello" } +// │ │ └─ StrChunk 1234.4!5 len:5 { "hello" } // │ └─ "flags" -// │ └─ ArrayRga "arr" 1234.9 -// │ └─ ArrayChunk 1234.14!2 len:2 -// │ ├─ [0]: ValueLww "val" 1234.11 -// │ │ └─ Const "con" 1234.10 { true } -// │ └─ [1]: ValueLww "val" 1234.13 -// │ └─ Const "con" 1234.12 { false } +// │ └─ ArrNode 1234.9 +// │ └─ ArrChunk 1234.14!2 len:2 +// │ ├─ [0]: ValNode 1234.11 +// │ │ └─ ConNode 1234.10 { true } +// │ └─ [1]: ValNode 1234.13 +// │ └─ ConNode 1234.12 { false } // │ // └─ VectorClock 1234.18 @@ -58,43 +58,43 @@ console.log(model.view().flags[0]); console.log(model.find.val.toApi() + ''); // ObjectApi -// └─ ObjectLww "obj" 1234.1 +// └─ ObjNode 1234.1 // ├─ "num" -// │ └─ Const "con" 1234.2 { 123 } +// │ └─ ConNode 1234.2 { 123 } // ├─ "text" -// │ └─ StringRga "str" 1234.3 { "hello" } -// │ └─ StringChunk 1234.4!5 len:5 { "hello" } +// │ └─ StrNode 1234.3 { "hello" } +// │ └─ StrChunk 1234.4!5 len:5 { "hello" } // └─ "flags" -// └─ ArrayRga "arr" 1234.9 -// └─ ArrayChunk 1234.14!2 len:2 -// ├─ [0]: ValueLww "val" 1234.11 -// │ └─ Const "con" 1234.10 { true } -// └─ [1]: ValueLww "val" 1234.13 -// └─ Const "con" 1234.12 { false } +// └─ ArrNode 1234.9 +// └─ ArrChunk 1234.14!2 len:2 +// ├─ [0]: ValNode 1234.11 +// │ └─ ConNode 1234.10 { true } +// └─ [1]: ValNode 1234.13 +// └─ ConNode 1234.12 { false } console.log(model.find.val.flags.toApi() + ''); -// ArrayApi -// └─ ArrayRga "arr" 1234.9 -// └─ ArrayChunk 1234.14!2 len:2 -// ├─ [0]: ValueLww "val" 1234.11 -// │ └─ Const "con" 1234.10 { true } -// └─ [1]: ValueLww "val" 1234.13 -// └─ Const "con" 1234.12 { false } +// ArrApi +// └─ ArrNode 1234.9 +// └─ ArrChunk 1234.14!2 len:2 +// ├─ [0]: ValNode 1234.11 +// │ └─ ConNode 1234.10 { true } +// └─ [1]: ValNode 1234.13 +// └─ ConNode 1234.12 { false } console.log(model.find.val.flags[1].toApi() + ''); -// ValueApi -// └─ ValueLww "val" 1234.13 -// └─ Const "con" 1234.12 { false } +// ValApi +// └─ ValNode 1234.13 +// └─ ConNode 1234.12 { false } console.log(model.find.val.flags[1].val.toApi() + ''); -// ConstApi -// └─ Const "con" 1234.12 { false } +// ConApi +// └─ ConNode 1234.12 { false } console.log(model.find.val.num.toApi() + ''); -// ConstApi -// └─ Const "con" 1234.2 { 123 } +// ConApi +// └─ ConNode 1234.2 { 123 } console.log(model.find.val.text.toApi() + ''); -// StringApi -// └─ StringRga "str" 1234.3 { "hello" } -// └─ StringChunk 1234.4!5 len:5 { "hello" } +// StrApi +// └─ StrNode 1234.3 { "hello" } +// └─ StrChunk 1234.4!5 len:5 { "hello" } diff --git a/src/json-crdt/__tests__/fuzzer/Picker.ts b/src/json-crdt/__tests__/fuzzer/Picker.ts index d74325facf..42a4ece843 100644 --- a/src/json-crdt/__tests__/fuzzer/Picker.ts +++ b/src/json-crdt/__tests__/fuzzer/Picker.ts @@ -1,10 +1,6 @@ import {DelOp, InsObjOp, InsStrOp, InsBinOp, InsArrOp} from '../../../json-crdt-patch/operations'; import {RandomJson} from '../../../json-random'; -import {JsonNode} from '../../types'; -import {ObjectLww} from '../../types/lww-object/ObjectLww'; -import {ArrayRga} from '../../types/rga-array/ArrayRga'; -import {BinaryRga} from '../../types/rga-binary/BinaryRga'; -import {StringRga} from '../../types/rga-string/StringRga'; +import {JsonNode, ObjNode, ArrNode, BinNode, StrNode} from '../../nodes'; import {Model} from '../../model/Model'; import {Fuzzer} from '../../../util/Fuzzer'; import {FuzzerOptions} from './types'; @@ -31,7 +27,7 @@ export class Picker { return Fuzzer.pick(nodes); } - public pickStringOperation(node: StringRga): StringOp { + public pickStringOperation(node: StrNode): StringOp { const length = node.length(); if (!length) return InsStrOp; if (length >= this.opts.maxStringLength) return DelOp; @@ -39,7 +35,7 @@ export class Picker { return InsStrOp; } - public pickBinaryOperation(node: BinaryRga): BinaryOp { + public pickBinaryOperation(node: BinNode): BinaryOp { const length = node.length(); if (!length) return InsBinOp; if (length >= this.opts.maxStringLength) return DelOp; @@ -47,7 +43,7 @@ export class Picker { return InsBinOp; } - public pickObjectOperation(node: ObjectLww): [key: string, opcode: ObjectOp] { + public pickObjectOperation(node: ObjNode): [key: string, opcode: ObjectOp] { if (!node.keys.size) return [this.generateObjectKey(), InsObjOp]; if (Math.random() > 0.45) return [this.generateObjectKey(), InsObjOp]; const keys = [...node.keys.keys()]; @@ -56,7 +52,7 @@ export class Picker { return [key, DelOp]; } - public pickArrayOperation(node: ArrayRga): ArrayOp { + public pickArrayOperation(node: ArrNode): ArrayOp { if (!node.length()) return InsArrOp; if (Math.random() > 0.45) return InsArrOp; else return DelOp; diff --git a/src/json-crdt/__tests__/fuzzer/SessionLogical.ts b/src/json-crdt/__tests__/fuzzer/SessionLogical.ts index 40e5f2ee9a..20c152d42e 100644 --- a/src/json-crdt/__tests__/fuzzer/SessionLogical.ts +++ b/src/json-crdt/__tests__/fuzzer/SessionLogical.ts @@ -1,5 +1,3 @@ -import {ArrayRga} from '../../types/rga-array/ArrayRga'; -import {BinaryRga} from '../../types/rga-binary/BinaryRga'; import {decode as decodeBinary, encode as encodeBinary} from '../../../json-crdt-patch/codec/binary'; import {decode as decodeCompact} from '../../../json-crdt-patch/codec/compact/decode'; import {decode as decodeJson} from '../../../json-crdt-patch/codec/verbose/decode'; @@ -14,14 +12,12 @@ import {Encoder as CompactEncoder} from '../../codec/structural/compact/Encoder' import {Encoder as JsonEncoder} from '../../codec/structural/json/Encoder'; import {generateInteger} from './util'; import {Model} from '../..'; -import {ObjectLww} from '../../types/lww-object/ObjectLww'; import {Patch} from '../../../json-crdt-patch/Patch'; import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; import {RandomJson} from '../../../json-random/RandomJson'; import {randomU32} from 'hyperdyperid/lib/randomU32'; -import {StringRga} from '../../types/rga-string/StringRga'; +import {StrNode, ValNode, ObjNode, ArrNode, BinNode} from '../../nodes'; import {interval} from '../../../json-crdt-patch/clock'; -import {ValueLww} from '../../types/lww-value/ValueLww'; import type {JsonCrdtFuzzer} from './JsonCrdtFuzzer'; import {Fuzzer} from '../../../util/Fuzzer'; @@ -71,11 +67,11 @@ export class SessionLogical { const model = this.models[peer]; const node = this.fuzzer.picker.pickNode(model); let patch: Patch | null = null; - if (node instanceof StringRga) patch = this.generateStringPatch(model, node); - else if (node instanceof BinaryRga) patch = this.generateBinaryPatch(model, node); - else if (node instanceof ObjectLww) patch = this.generateObjectPatch(model, node); - else if (node instanceof ArrayRga) patch = this.generateArrayPatch(model, node); - else if (node instanceof ValueLww) patch = this.generateValuePatch(model, node); + if (node instanceof StrNode) patch = this.generateStringPatch(model, node); + else if (node instanceof BinNode) patch = this.generateBinaryPatch(model, node); + else if (node instanceof ObjNode) patch = this.generateObjectPatch(model, node); + else if (node instanceof ArrNode) patch = this.generateArrayPatch(model, node); + else if (node instanceof ValNode) patch = this.generateValuePatch(model, node); else return; if (!patch) return; model.applyPatch(patch); @@ -90,7 +86,7 @@ export class SessionLogical { if (this.debug) this.patchesSerialized[peer].push(encodeJson(patch)); } - private generateStringPatch(model: Model, node: StringRga): Patch | null { + private generateStringPatch(model: Model, node: StrNode): Patch | null { const opcode = this.fuzzer.picker.pickStringOperation(node); const builder = new PatchBuilder(model.clock); const size = node.length(); @@ -109,7 +105,7 @@ export class SessionLogical { return builder.patch; } - private generateBinaryPatch(model: Model, node: BinaryRga): Patch | null { + private generateBinaryPatch(model: Model, node: BinNode): Patch | null { const opcode = this.fuzzer.picker.pickBinaryOperation(node); const builder = new PatchBuilder(model.clock); const size = node.length(); @@ -128,7 +124,7 @@ export class SessionLogical { return builder.patch; } - private generateObjectPatch(model: Model, node: ObjectLww): Patch { + private generateObjectPatch(model: Model, node: ObjNode): Patch { const [key, opcode] = this.fuzzer.picker.pickObjectOperation(node); const builder = new PatchBuilder(model.clock); if (opcode === InsObjOp) { @@ -154,7 +150,7 @@ export class SessionLogical { return builder.patch; } - private generateArrayPatch(model: Model, node: ArrayRga): Patch { + private generateArrayPatch(model: Model, node: ArrNode): Patch { const opcode = this.fuzzer.picker.pickArrayOperation(node); const builder = new PatchBuilder(model.clock); const length = node.length(); @@ -179,7 +175,7 @@ export class SessionLogical { return builder.patch; } - private generateValuePatch(model: Model, node: ValueLww): Patch { + private generateValuePatch(model: Model, node: ValNode): Patch { const builder = new PatchBuilder(model.clock); const value = Math.random() > 0.1 diff --git a/src/json-crdt/__tests__/hash.spec.ts b/src/json-crdt/__tests__/hash.spec.ts index ad7b097937..f119f6338d 100644 --- a/src/json-crdt/__tests__/hash.spec.ts +++ b/src/json-crdt/__tests__/hash.spec.ts @@ -2,7 +2,7 @@ import {RandomJson} from '../../json-random'; import {hashNode} from '../hash'; import {Model} from '../model'; -test('can compute Const hash', () => { +test('can compute ConNode hash', () => { const model = Model.withLogicalClock(); model.api.root(123); const c1 = model.api.val([]).in(); diff --git a/src/json-crdt/codec/indexed/binary/Decoder.ts b/src/json-crdt/codec/indexed/binary/Decoder.ts index b537d6b9c7..ad683fe294 100644 --- a/src/json-crdt/codec/indexed/binary/Decoder.ts +++ b/src/json-crdt/codec/indexed/binary/Decoder.ts @@ -1,16 +1,21 @@ -import {ArrayChunk, ArrayRga} from '../../../types/rga-array/ArrayRga'; -import {BinaryChunk, BinaryRga} from '../../../types/rga-binary/BinaryRga'; +import { + ConNode, + JsonNode, + ValNode, + ArrNode, + ArrChunk, + BinNode, + BinChunk, + ObjNode, + StrNode, + StrChunk, +} from '../../../nodes'; import {ClockTable} from '../../../../json-crdt-patch/codec/clock/ClockTable'; -import {Const} from '../../../types/const/Const'; import {CrdtReader} from '../../../../json-crdt-patch/util/binary/CrdtDecoder'; import {IndexedFields, FieldName, IndexedNodeFields} from './types'; import {ITimestampStruct, IVectorClock, Timestamp, VectorClock} from '../../../../json-crdt-patch/clock'; -import {JsonNode} from '../../../types'; import {Model, UNDEFINED} from '../../../model/Model'; import {MsgPackDecoderFast} from '../../../../json-pack/msgpack'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {StringChunk, StringRga} from '../../../types/rga-string/StringRga'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; export class Decoder { public readonly dec = new MsgPackDecoderFast(new CrdtReader()); @@ -76,7 +81,7 @@ export class Decoder { case 0xd4: return this.cConst(id); case 0xd5: - return new Const(id, this.ts()); + return new ConNode(id, this.ts()); case 0xd6: return this.cVal(id); case 0xde: @@ -99,19 +104,19 @@ export class Decoder { return UNDEFINED; } - public cConst(id: ITimestampStruct): Const { + public cConst(id: ITimestampStruct): ConNode { const val = this.dec.val(); - return new Const(id, val); + return new ConNode(id, val); } - public cVal(id: ITimestampStruct): ValueLww { + public cVal(id: ITimestampStruct): ValNode { const val = this.ts(); - return new ValueLww(this.doc, id, val); + return new ValNode(this.doc, id, val); } - public cObj(id: ITimestampStruct, length: number): ObjectLww { + public cObj(id: ITimestampStruct, length: number): ObjNode { const decoder = this.dec; - const obj = new ObjectLww(this.doc, id); + const obj = new ObjNode(this.doc, id); const keys = obj.keys; for (let i = 0; i < length; i++) { const key = String(decoder.val()); @@ -121,44 +126,44 @@ export class Decoder { return obj; } - protected cStr(id: ITimestampStruct, length: number): StringRga { + protected cStr(id: ITimestampStruct, length: number): StrNode { const decoder = this.dec; - const node = new StringRga(id); + const node = new StrNode(id); node.ingest(length, () => { const chunkId = this.ts(); const val = decoder.val(); - if (typeof val === 'number') return new StringChunk(chunkId, val, ''); + if (typeof val === 'number') return new StrChunk(chunkId, val, ''); const data = String(val); - return new StringChunk(chunkId, data.length, data); + return new StrChunk(chunkId, data.length, data); }); return node; } - protected cBin(id: ITimestampStruct, length: number): BinaryRga { + protected cBin(id: ITimestampStruct, length: number): BinNode { const decoder = this.dec; const reader = decoder.reader; - const node = new BinaryRga(id); + const node = new BinNode(id); node.ingest(length, () => { const chunkId = this.ts(); const [deleted, length] = reader.b1vu28(); - if (deleted) return new BinaryChunk(chunkId, length, undefined); + if (deleted) return new BinChunk(chunkId, length, undefined); const data = reader.buf(length); - return new BinaryChunk(chunkId, length, data); + return new BinChunk(chunkId, length, data); }); return node; } - protected cArr(id: ITimestampStruct, length: number): ArrayRga { + protected cArr(id: ITimestampStruct, length: number): ArrNode { const decoder = this.dec; const reader = decoder.reader; - const node = new ArrayRga(this.doc, id); + const node = new ArrNode(this.doc, id); node.ingest(length, () => { const chunkId = this.ts(); const [deleted, length] = reader.b1vu28(); - if (deleted) return new ArrayChunk(chunkId, length, undefined); + if (deleted) return new ArrChunk(chunkId, length, undefined); const data: ITimestampStruct[] = []; for (let i = 0; i < length; i++) data.push(this.ts()); - return new ArrayChunk(chunkId, length, data); + return new ArrChunk(chunkId, length, data); }); return node; } diff --git a/src/json-crdt/codec/indexed/binary/Encoder.ts b/src/json-crdt/codec/indexed/binary/Encoder.ts index 3150cc94e7..e2afb9e361 100644 --- a/src/json-crdt/codec/indexed/binary/Encoder.ts +++ b/src/json-crdt/codec/indexed/binary/Encoder.ts @@ -3,13 +3,7 @@ import {ClockTable} from '../../../../json-crdt-patch/codec/clock/ClockTable'; import {CrdtWriter} from '../../../../json-crdt-patch/util/binary/CrdtEncoder'; import {MsgPackEncoder} from '../../../../json-pack/msgpack'; import {Model} from '../../../model'; -import {JsonNode} from '../../../types'; -import {Const} from '../../../types/const/Const'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; -import {ArrayRga} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga} from '../../../types/rga-binary/BinaryRga'; -import {StringRga} from '../../../types/rga-string/StringRga'; +import {ConNode, JsonNode, ValNode, ArrNode, BinNode, ObjNode, StrNode} from '../../../nodes'; import {IndexedFields, FieldName} from './types'; const EMPTY = new Uint8Array(0); @@ -50,12 +44,12 @@ export class Encoder { }; public encodeNode(node: JsonNode): Uint8Array { - if (node instanceof ValueLww) return this.encodeVal(node); - else if (node instanceof Const) return this.encodeConst(node); - else if (node instanceof StringRga) return this.encodeStr(node); - else if (node instanceof ObjectLww) return this.encodeObj(node); - else if (node instanceof ArrayRga) return this.encodeArr(node); - else if (node instanceof BinaryRga) return this.encodeBin(node); + if (node instanceof ValNode) return this.encodeVal(node); + else if (node instanceof ConNode) return this.encodeConst(node); + else if (node instanceof StrNode) return this.encodeStr(node); + else if (node instanceof ObjNode) return this.encodeObj(node); + else if (node instanceof ArrNode) return this.encodeArr(node); + else if (node instanceof BinNode) return this.encodeBin(node); else return EMPTY; } @@ -64,7 +58,7 @@ export class Encoder { this.enc.writer.id(index, id.time); } - public encodeVal(node: ValueLww): Uint8Array { + public encodeVal(node: ValNode): Uint8Array { const writer = this.enc.writer; const child = node.node(); writer.reset(); @@ -73,7 +67,7 @@ export class Encoder { return writer.flush(); } - public encodeConst(node: Const): Uint8Array { + public encodeConst(node: ConNode): Uint8Array { const encoder = this.enc; const writer = encoder.writer; const val = node.val; @@ -88,7 +82,7 @@ export class Encoder { return writer.flush(); } - public encodeStr(node: StringRga): Uint8Array { + public encodeStr(node: StrNode): Uint8Array { const encoder = this.enc; const writer = encoder.writer; writer.reset(); @@ -101,7 +95,7 @@ export class Encoder { return writer.flush(); } - public encodeBin(node: BinaryRga): Uint8Array { + public encodeBin(node: BinNode): Uint8Array { const encoder = this.enc; const writer = encoder.writer; writer.reset(); @@ -117,7 +111,7 @@ export class Encoder { return writer.flush(); } - public encodeObj(node: ObjectLww): Uint8Array { + public encodeObj(node: ObjNode): Uint8Array { const encoder = this.enc; const writer = encoder.writer; writer.reset(); @@ -131,7 +125,7 @@ export class Encoder { this.ts(value); }; - public encodeArr(node: ArrayRga): Uint8Array { + public encodeArr(node: ArrNode): Uint8Array { const encoder = this.enc; const writer = encoder.writer; writer.reset(); diff --git a/src/json-crdt/codec/structural/binary/Decoder.ts b/src/json-crdt/codec/structural/binary/Decoder.ts index e6376118cf..c0c66194bc 100644 --- a/src/json-crdt/codec/structural/binary/Decoder.ts +++ b/src/json-crdt/codec/structural/binary/Decoder.ts @@ -1,18 +1,23 @@ -import {ArrayRga, ArrayChunk} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga, BinaryChunk} from '../../../types/rga-binary/BinaryRga'; import {ClockDecoder} from '../../../../json-crdt-patch/codec/clock/ClockDecoder'; -import {Const} from '../../../types/const/Const'; import {CrdtReader} from '../../../../json-crdt-patch/util/binary/CrdtDecoder'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; import {Model, UNDEFINED} from '../../../model/Model'; import {MsgPackDecoderFast} from '../../../../json-pack/msgpack'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {RootLww} from '../../../types/lww-root/RootLww'; import {SESSION} from '../../../../json-crdt-patch/constants'; -import {StringRga, StringChunk} from '../../../types/rga-string/StringRga'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; -import type {JsonNode} from '../../../types'; +import { + ArrNode, + ArrChunk, + BinNode, + BinChunk, + ConNode, + ObjNode, + RootNode, + StrNode, + StrChunk, + ValNode, + VecNode, + type JsonNode, +} from '../../../nodes'; export class Decoder extends MsgPackDecoderFast { protected doc!: Model; @@ -37,7 +42,7 @@ export class Decoder extends MsgPackDecoderFast { const clock = this.clockDecoder!.clock; this.doc = Model.withLogicalClock(clock); } - this.doc.root = new RootLww(this.doc, this.cRoot().id); + this.doc.root = new RootNode(this.doc, this.cRoot().id); delete this.clockDecoder; return this.doc; } @@ -90,18 +95,18 @@ export class Decoder extends MsgPackDecoderFast { case 0xc6: return this.cBin(id, reader.u32()); case 0xd4: { - const obj = new Const(id, this.val()); + const obj = new ConNode(id, this.val()); this.doc.index.set(id, obj); return obj; } case 0xd5: { - const obj = new Const(id, this.ts()); + const obj = new ConNode(id, this.ts()); this.doc.index.set(id, obj); return obj; } case 0xd6: { const val = this.cNode(); - const obj = new ValueLww(this.doc, id, val.id); + const obj = new ValNode(this.doc, id, val.id); this.doc.index.set(id, obj); return obj; } @@ -126,23 +131,23 @@ export class Decoder extends MsgPackDecoderFast { throw new Error('UNKNOWN_NODE'); } - public cObj(id: ITimestampStruct, length: number): ObjectLww { - const obj = new ObjectLww(this.doc, id); + public cObj(id: ITimestampStruct, length: number): ObjNode { + const obj = new ObjNode(this.doc, id); for (let i = 0; i < length; i++) this.cObjChunk(obj); this.doc.index.set(id, obj); return obj; } - private cObjChunk(obj: ObjectLww): void { + private cObjChunk(obj: ObjNode): void { const key: string = this.key(); obj.keys.set(key, this.cNode().id); } - public cTup(id: ITimestampStruct): ArrayLww { + public cTup(id: ITimestampStruct): VecNode { const reader = this.reader; const length = this.reader.u8(); reader.x++; - const obj = new ArrayLww(this.doc, id); + const obj = new VecNode(this.doc, id); const elements = obj.elements; for (let i = 0; i < length; i++) { const octet = reader.peak(); @@ -155,54 +160,54 @@ export class Decoder extends MsgPackDecoderFast { return obj; } - public cArr(id: ITimestampStruct, length: number): ArrayRga { - const obj = new ArrayRga(this.doc, id); + public cArr(id: ITimestampStruct, length: number): ArrNode { + const obj = new ArrNode(this.doc, id); obj.ingest(length, this.cArrChunk); this.doc.index.set(id, obj); return obj; } - private readonly cArrChunk = (): ArrayChunk => { + private readonly cArrChunk = (): ArrChunk => { const [deleted, length] = this.reader.b1vu28(); const id = this.ts(); - if (deleted) return new ArrayChunk(id, length, undefined); + if (deleted) return new ArrChunk(id, length, undefined); const ids: ITimestampStruct[] = []; for (let i = 0; i < length; i++) ids.push(this.cNode().id); - return new ArrayChunk(id, length, ids); + return new ArrChunk(id, length, ids); }; - public cStr(id: ITimestampStruct, length: number): StringRga { - const node = new StringRga(id); + public cStr(id: ITimestampStruct, length: number): StrNode { + const node = new StrNode(id); if (length) node.ingest(length, this.cStrChunk); this.doc.index.set(id, node); return node; } - private cStrChunk = (): StringChunk => { + private cStrChunk = (): StrChunk => { const reader = this.reader; const id = this.ts(); const isTombstone = reader.uint8[reader.x] === 0; if (isTombstone) { reader.x++; const length = reader.vu39(); - return new StringChunk(id, length, ''); + return new StrChunk(id, length, ''); } const text: string = this.str() as string; - return new StringChunk(id, text.length, text); + return new StrChunk(id, text.length, text); }; - public cBin(id: ITimestampStruct, length: number): BinaryRga { - const node = new BinaryRga(id); + public cBin(id: ITimestampStruct, length: number): BinNode { + const node = new BinNode(id); if (length) node.ingest(length, this.cBinChunk); this.doc.index.set(id, node); return node; } - private cBinChunk = (): BinaryChunk => { + private cBinChunk = (): BinChunk => { const reader = this.reader; const [deleted, length] = reader.b1vu28(); const id = this.ts(); - if (deleted) return new BinaryChunk(id, length, undefined); - else return new BinaryChunk(id, length, reader.buf(length)); + if (deleted) return new BinChunk(id, length, undefined); + else return new BinChunk(id, length, reader.buf(length)); }; } diff --git a/src/json-crdt/codec/structural/binary/Encoder.ts b/src/json-crdt/codec/structural/binary/Encoder.ts index eeabf31993..c242accc6d 100644 --- a/src/json-crdt/codec/structural/binary/Encoder.ts +++ b/src/json-crdt/codec/structural/binary/Encoder.ts @@ -1,17 +1,9 @@ -import {ArrayRga} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga} from '../../../types/rga-binary/BinaryRga'; +import {ConNode, RootNode, JsonNode, ValNode, VecNode, ArrNode, BinNode, ObjNode, StrNode} from '../../../nodes'; import {ClockEncoder} from '../../../../json-crdt-patch/codec/clock/ClockEncoder'; -import {Const} from '../../../types/const/Const'; import {CrdtWriter} from '../../../../json-crdt-patch/util/binary/CrdtEncoder'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; -import {JsonNode} from '../../../types'; import {MsgPackEncoder} from '../../../../json-pack/msgpack'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {RootLww} from '../../../types/lww-root/RootLww'; import {SESSION} from '../../../../json-crdt-patch/constants'; -import {StringRga} from '../../../types/rga-string/StringRga'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; import type {Model} from '../../../model'; export class Encoder extends MsgPackEncoder { @@ -78,7 +70,7 @@ export class Encoder extends MsgPackEncoder { protected ts: (ts: ITimestampStruct) => void = this.tsLogical; - protected cRoot(root: RootLww): void { + protected cRoot(root: RootNode): void { const val = root.val; if (val.sid === SESSION.SYSTEM) this.writer.u8(0); else this.cNode(root.node()); @@ -86,16 +78,16 @@ export class Encoder extends MsgPackEncoder { protected cNode(node: JsonNode): void { // TODO: PERF: use a switch - if (node instanceof Const) this.cConst(node); - else if (node instanceof ValueLww) this.cVal(node); - else if (node instanceof StringRga) this.cStr(node); - else if (node instanceof ObjectLww) this.cObj(node); - else if (node instanceof ArrayLww) this.cTup(node); - else if (node instanceof ArrayRga) this.cArr(node); - else if (node instanceof BinaryRga) this.cBin(node); + if (node instanceof ConNode) this.cConst(node); + else if (node instanceof ValNode) this.cVal(node); + else if (node instanceof StrNode) this.cStr(node); + else if (node instanceof ObjNode) this.cObj(node); + else if (node instanceof VecNode) this.cTup(node); + else if (node instanceof ArrNode) this.cArr(node); + else if (node instanceof BinNode) this.cBin(node); } - protected cObj(obj: ObjectLww): void { + protected cObj(obj: ObjNode): void { this.ts(obj.id); this.writeObjHdr(obj.keys.size); obj.keys.forEach(this.cKey); @@ -106,7 +98,7 @@ export class Encoder extends MsgPackEncoder { this.cNode(this.doc.index.get(val)!); }; - protected cTup(obj: ArrayLww): void { + protected cTup(obj: VecNode): void { this.ts(obj.id); const elements = obj.elements; const length = elements.length; @@ -120,7 +112,7 @@ export class Encoder extends MsgPackEncoder { } } - protected cArr(obj: ArrayRga): void { + protected cArr(obj: ArrNode): void { const ts = this.ts; const writer = this.writer; ts(obj.id); @@ -137,7 +129,7 @@ export class Encoder extends MsgPackEncoder { } } - protected cStr(obj: StringRga): void { + protected cStr(obj: StrNode): void { const ts = this.ts; const writer = this.writer; ts(obj.id); @@ -152,7 +144,7 @@ export class Encoder extends MsgPackEncoder { } } - protected cBin(obj: BinaryRga): void { + protected cBin(obj: BinNode): void { const ts = this.ts; const writer = this.writer; ts(obj.id); @@ -168,13 +160,13 @@ export class Encoder extends MsgPackEncoder { } } - protected cVal(obj: ValueLww): void { + protected cVal(obj: ValNode): void { this.ts(obj.id); this.writer.u8(0xd6); this.cNode(obj.node()); } - protected cConst(obj: Const): void { + protected cConst(obj: ConNode): void { this.ts(obj.id); const val = obj.val; if (val instanceof Timestamp) { diff --git a/src/json-crdt/codec/structural/compact/Decoder.ts b/src/json-crdt/codec/structural/compact/Decoder.ts index 8eaf2a569b..fe7a8154b0 100644 --- a/src/json-crdt/codec/structural/compact/Decoder.ts +++ b/src/json-crdt/codec/structural/compact/Decoder.ts @@ -1,17 +1,9 @@ -import {ArrayRga, ArrayChunk} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga, BinaryChunk} from '../../../types/rga-binary/BinaryRga'; +import * as nodes from '../../../nodes'; import {ClockDecoder} from '../../../../json-crdt-patch/codec/clock/ClockDecoder'; -import {Const} from '../../../types/const/Const'; -import {RootLww} from '../../../types/lww-root/RootLww'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; -import {JsonNode} from '../../../types'; import {Model, UNDEFINED} from '../../../model/Model'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; import {ORIGIN, SESSION} from '../../../../json-crdt-patch/constants'; -import {StringRga, StringChunk} from '../../../types/rga-string/StringRga'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; import {Code} from '../../../../json-crdt-patch/codec/compact/constants'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; export class Decoder { protected time?: number; @@ -27,7 +19,7 @@ export class Decoder { } const doc = isServerTime ? Model.withServerClock(x as number) : Model.withLogicalClock(this.clockDecoder!.clock); const val = data[1] ? this.decodeNode(doc, data[1]) : UNDEFINED; - doc.root = new RootLww(doc, val.id); + doc.root = new nodes.RootNode(doc, val.id); return doc; } @@ -52,7 +44,7 @@ export class Decoder { } } - protected decodeNode(doc: Model, data: unknown): JsonNode { + protected decodeNode(doc: Model, data: unknown): nodes.JsonNode { if (data instanceof Array) { switch (data[0]) { case Code.MakeObject: @@ -76,9 +68,9 @@ export class Decoder { throw new Error('UNKNOWN_NODE'); } - protected decodeObj(doc: Model, data: unknown[]): ObjectLww { + protected decodeObj(doc: Model, data: unknown[]): nodes.ObjNode { const [id, index] = this.ts(data, 1); - const obj = new ObjectLww(doc, id); + const obj = new nodes.ObjNode(doc, id); const length = data.length; for (let i = index; i < length; ) { const key = data[i] as string; @@ -90,9 +82,9 @@ export class Decoder { return obj; } - protected decodeTup(doc: Model, data: unknown[]): ArrayLww { + protected decodeTup(doc: Model, data: unknown[]): nodes.VecNode { const [id, index] = this.ts(data, 1); - const obj = new ArrayLww(doc, id); + const obj = new nodes.VecNode(doc, id); const length = data.length; const elements = obj.elements; for (let i = index; i < length; ) { @@ -107,79 +99,79 @@ export class Decoder { return obj; } - protected decodeArr(doc: Model, data: unknown[]): ArrayRga { + protected decodeArr(doc: Model, data: unknown[]): nodes.ArrNode { const size = data[1] as number; const [id, index] = this.ts(data, 2); - const obj = new ArrayRga(doc, id); + const obj = new nodes.ArrNode(doc, id); const self = this; let i = index; obj.ingest(size, () => { const [chunkId, idx] = self.ts(data, i); const content = data[idx]; i = idx + 1; - if (typeof content === 'number') return new ArrayChunk(chunkId, content, undefined); + if (typeof content === 'number') return new nodes.ArrChunk(chunkId, content, undefined); const ids = (content as unknown[]).map((c) => this.decodeNode(doc, c).id); - return new ArrayChunk(chunkId, (content as string).length, ids); + return new nodes.ArrChunk(chunkId, (content as string).length, ids); }); doc.index.set(id, obj); return obj; } - protected decodeStr(doc: Model, data: unknown[]): StringRga { + protected decodeStr(doc: Model, data: unknown[]): nodes.StrNode { const size = data[1] as number; const [id, index] = this.ts(data, 2); - const node = new StringRga(id); + const node = new nodes.StrNode(id); const self = this; let i = index; node.ingest(size, () => { const [chunkId, idx] = self.ts(data, i); const content = data[idx]; i = idx + 1; - if (typeof content === 'number') return new StringChunk(chunkId, content, ''); - return new StringChunk(chunkId, (content as string).length, content as string); + if (typeof content === 'number') return new nodes.StrChunk(chunkId, content, ''); + return new nodes.StrChunk(chunkId, (content as string).length, content as string); }); doc.index.set(id, node); return node; } - protected decodeBin(doc: Model, data: unknown[]): BinaryRga { + protected decodeBin(doc: Model, data: unknown[]): nodes.BinNode { const size = data[1] as number; const [id, index] = this.ts(data, 2); - const node = new BinaryRga(id); + const node = new nodes.BinNode(id); const self = this; let i = index; node.ingest(size, () => { const [chunkId, idx] = self.ts(data, i); const content = data[idx]; i = idx + 1; - if (typeof content === 'number') return new BinaryChunk(chunkId, content, undefined); + if (typeof content === 'number') return new nodes.BinChunk(chunkId, content, undefined); const buf = content as Uint8Array; - return new BinaryChunk(chunkId, buf.length, buf); + return new nodes.BinChunk(chunkId, buf.length, buf); }); doc.index.set(id, node); return node; } - protected decodeVal(doc: Model, data: unknown[]): ValueLww { + protected decodeVal(doc: Model, data: unknown[]): nodes.ValNode { const [id, index] = this.ts(data, 1); const child = this.decodeNode(doc, data[index]); - const obj = new ValueLww(doc, id, child.id); + const obj = new nodes.ValNode(doc, id, child.id); doc.index.set(id, obj); return obj; } - protected decodeConst(doc: Model, data: unknown[]): Const { + protected decodeConst(doc: Model, data: unknown[]): nodes.ConNode { const [id, index] = this.ts(data, 1); const value = data[index]; - const obj = new Const(id, value); + const obj = new nodes.ConNode(id, value); doc.index.set(id, obj); return obj; } - protected decodeConstId(doc: Model, data: unknown[]): Const { + protected decodeConstId(doc: Model, data: unknown[]): nodes.ConNode { const [id, index] = this.ts(data, 1); const val = this.ts(data, index)[0]; - const obj = new Const(id, val); + const obj = new nodes.ConNode(id, val); doc.index.set(id, obj); return obj; } diff --git a/src/json-crdt/codec/structural/compact/Encoder.ts b/src/json-crdt/codec/structural/compact/Encoder.ts index 0fef8fdcce..beba774d04 100644 --- a/src/json-crdt/codec/structural/compact/Encoder.ts +++ b/src/json-crdt/codec/structural/compact/Encoder.ts @@ -1,17 +1,9 @@ -import {ArrayRga, ArrayChunk} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga, BinaryChunk} from '../../../types/rga-binary/BinaryRga'; +import * as nodes from '../../../nodes'; import {ClockEncoder} from '../../../../json-crdt-patch/codec/clock/ClockEncoder'; -import {Const} from '../../../types/const/Const'; -import {RootLww} from '../../../types/lww-root/RootLww'; import {ITimestampStruct, Timestamp} from '../../../../json-crdt-patch/clock'; -import {JsonNode} from '../../../types'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; import {SESSION} from '../../../../json-crdt-patch/constants'; -import {StringRga, StringChunk} from '../../../types/rga-string/StringRga'; import {Code} from '../../../../json-crdt-patch/codec/compact/constants'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; import type {Model} from '../../../model'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; export class Encoder { protected time?: number; @@ -51,24 +43,24 @@ export class Encoder { } } - protected encodeRoot(arr: unknown[], root: RootLww): void { + protected encodeRoot(arr: unknown[], root: nodes.RootNode): void { if (!root.val.time) arr.push(0); else this.cNode(arr, root.node()); } - protected cNode(arr: unknown[], node: JsonNode): void { + protected cNode(arr: unknown[], node: nodes.JsonNode): void { // TODO: PERF: use switch with `node.constructor`. - if (node instanceof ObjectLww) return this.encodeObj(arr, node); - else if (node instanceof ArrayRga) return this.encodeArr(arr, node); - else if (node instanceof StringRga) return this.encodeStr(arr, node); - else if (node instanceof ValueLww) return this.cVal(arr, node); - else if (node instanceof ArrayLww) return this.cTup(arr, node); - else if (node instanceof Const) return this.cConst(arr, node); - else if (node instanceof BinaryRga) return this.encodeBin(arr, node); + if (node instanceof nodes.ObjNode) return this.encodeObj(arr, node); + else if (node instanceof nodes.ArrNode) return this.encodeArr(arr, node); + else if (node instanceof nodes.StrNode) return this.encodeStr(arr, node); + else if (node instanceof nodes.ValNode) return this.cVal(arr, node); + else if (node instanceof nodes.VecNode) return this.cTup(arr, node); + else if (node instanceof nodes.ConNode) return this.cConst(arr, node); + else if (node instanceof nodes.BinNode) return this.encodeBin(arr, node); throw new Error('UNKNOWN_NODE'); } - protected encodeObj(arr: unknown[], obj: ObjectLww): void { + protected encodeObj(arr: unknown[], obj: nodes.ObjNode): void { const res: unknown[] = [Code.MakeObject]; arr.push(res); this.ts(res, obj.id); @@ -78,7 +70,7 @@ export class Encoder { }); } - protected cTup(arr: unknown[], obj: ArrayLww): void { + protected cTup(arr: unknown[], obj: nodes.VecNode): void { const res: unknown[] = [Code.MakeTuple]; arr.push(res); this.ts(res, obj.id); @@ -95,7 +87,7 @@ export class Encoder { } } - protected encodeArr(arr: unknown[], obj: ArrayRga): void { + protected encodeArr(arr: unknown[], obj: nodes.ArrNode): void { const res: unknown[] = [Code.MakeArray, obj.size()]; arr.push(res); this.ts(res, obj.id); @@ -104,7 +96,7 @@ export class Encoder { while ((chunk = iterator())) this.encodeArrChunk(res, chunk); } - protected encodeArrChunk(arr: unknown[], chunk: ArrayChunk): void { + protected encodeArrChunk(arr: unknown[], chunk: nodes.ArrChunk): void { this.ts(arr, chunk.id); if (chunk.del) arr.push(chunk.span); else { @@ -115,42 +107,42 @@ export class Encoder { } } - protected encodeStr(arr: unknown[], obj: StringRga): void { + protected encodeStr(arr: unknown[], obj: nodes.StrNode): void { const res: unknown[] = [Code.MakeString, obj.size()]; arr.push(res); this.ts(res, obj.id); const iterator = obj.iterator(); let chunk; - while ((chunk = iterator())) this.encodeStrChunk(res, chunk as StringChunk); + while ((chunk = iterator())) this.encodeStrChunk(res, chunk as nodes.StrChunk); } - protected encodeStrChunk(arr: unknown[], chunk: StringChunk): void { + protected encodeStrChunk(arr: unknown[], chunk: nodes.StrChunk): void { this.ts(arr, chunk.id); arr.push(chunk.del ? chunk.span : chunk.data!); } - protected encodeBin(arr: unknown[], obj: BinaryRga): void { + protected encodeBin(arr: unknown[], obj: nodes.BinNode): void { const res: unknown[] = [Code.MakeBinary, obj.size()]; arr.push(res); this.ts(res, obj.id); const iterator = obj.iterator(); let chunk; - while ((chunk = iterator())) this.encodeBinChunk(res, chunk as BinaryChunk); + while ((chunk = iterator())) this.encodeBinChunk(res, chunk as nodes.BinChunk); } - protected encodeBinChunk(arr: unknown[], chunk: BinaryChunk): void { + protected encodeBinChunk(arr: unknown[], chunk: nodes.BinChunk): void { this.ts(arr, chunk.id); arr.push(chunk.del ? chunk.span : chunk.data!); } - protected cVal(arr: unknown[], obj: ValueLww): void { + protected cVal(arr: unknown[], obj: nodes.ValNode): void { const res: unknown[] = [Code.MakeValue]; arr.push(res); this.ts(res, obj.id); this.cNode(res, obj.node()); } - protected cConst(arr: unknown[], obj: Const): void { + protected cConst(arr: unknown[], obj: nodes.ConNode): void { const val = obj.val; const res: unknown[] = []; if (val instanceof Timestamp) { diff --git a/src/json-crdt/codec/structural/json/Decoder.ts b/src/json-crdt/codec/structural/json/Decoder.ts index bf4afec93e..d7cdb9c885 100644 --- a/src/json-crdt/codec/structural/json/Decoder.ts +++ b/src/json-crdt/codec/structural/json/Decoder.ts @@ -1,15 +1,8 @@ -import {ArrayRga, ArrayChunk} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga, BinaryChunk} from '../../../types/rga-binary/BinaryRga'; -import {Const} from '../../../types/const/Const'; -import {RootLww} from '../../../types/lww-root/RootLww'; +import * as nodes from '../../../nodes'; import {fromBase64} from '../../../../util/base64/fromBase64'; import {ITimestampStruct, ts, VectorClock} from '../../../../json-crdt-patch/clock'; -import {JsonNode} from '../../../types'; import {Model} from '../../../model'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; import {SESSION} from '../../../../json-crdt-patch/constants'; -import {StringRga, StringChunk} from '../../../types/rga-string/StringRga'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; import { JsonCrdtNode, ObjectJsonCrdtNode, @@ -27,7 +20,6 @@ import { JsonCrdtTimestamp, TupleJsonCrdtNode, } from './types'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; export class Decoder { public decode({time, root}: JsonCrdtSnapshot): Model { @@ -55,12 +47,12 @@ export class Decoder { } protected cRoot(doc: Model, {node}: ValueJsonCrdtNode): void { - const val = node ? this.cNode(doc, node) : new Const(doc.clock.tick(0), null); - const root = new RootLww(doc, val.id); + const val = node ? this.cNode(doc, node) : new nodes.ConNode(doc.clock.tick(0), null); + const root = new nodes.RootNode(doc, val.id); doc.root = root; } - protected cNode(doc: Model, node: JsonCrdtNode): JsonNode { + protected cNode(doc: Model, node: JsonCrdtNode): nodes.JsonNode { switch (node.type) { case 'obj': return this.cObj(doc, node); @@ -80,9 +72,9 @@ export class Decoder { throw new Error('UNKNOWN_NODE'); } - protected cObj(doc: Model, node: ObjectJsonCrdtNode): ObjectLww { + protected cObj(doc: Model, node: ObjectJsonCrdtNode): nodes.ObjNode { const id = this.cTs(node.id); - const obj = new ObjectLww(doc, id); + const obj = new nodes.ObjNode(doc, id); const keys = Object.keys(node.keys); for (const key of keys) { const keyNode = node.keys[key]; @@ -92,9 +84,9 @@ export class Decoder { return obj; } - protected cTup(doc: Model, node: TupleJsonCrdtNode): ArrayLww { + protected cTup(doc: Model, node: TupleJsonCrdtNode): nodes.VecNode { const id = this.cTs(node.id); - const obj = new ArrayLww(doc, id); + const obj = new nodes.VecNode(doc, id); const elements = obj.elements; const components = node.components; const length = components.length; @@ -107,9 +99,9 @@ export class Decoder { return obj; } - protected cArr(doc: Model, node: ArrayJsonCrdtNode): ArrayRga { + protected cArr(doc: Model, node: ArrayJsonCrdtNode): nodes.ArrNode { const id = this.cTs(node.id); - const rga = new ArrayRga(doc, id); + const rga = new nodes.ArrNode(doc, id); const chunks = node.chunks; const length = chunks.length; if (length) { @@ -119,10 +111,10 @@ export class Decoder { const c = chunks[i++]; const id = self.cTs(c.id); if (typeof (c as JsonCrdtRgaTombstone).span === 'number') - return new ArrayChunk(id, (c as JsonCrdtRgaTombstone).span, undefined); + return new nodes.ArrChunk(id, (c as JsonCrdtRgaTombstone).span, undefined); else { const ids = (c as ArrayJsonCrdtChunk).nodes.map((n) => this.cNode(doc, n).id); - return new ArrayChunk(id, ids.length, ids); + return new nodes.ArrChunk(id, ids.length, ids); } }); } @@ -130,9 +122,9 @@ export class Decoder { return rga; } - protected cStr(doc: Model, node: StringJsonCrdtNode): StringRga { + protected cStr(doc: Model, node: StringJsonCrdtNode): nodes.StrNode { const id = this.cTs(node.id); - const rga = new StringRga(id); + const rga = new nodes.StrNode(id); const chunks = node.chunks; const length = chunks.length; if (length) { @@ -142,10 +134,10 @@ export class Decoder { const c = chunks[i++]; const id = self.cTs(c.id); if (typeof (c as JsonCrdtRgaTombstone).span === 'number') - return new StringChunk(id, (c as JsonCrdtRgaTombstone).span, ''); + return new nodes.StrChunk(id, (c as JsonCrdtRgaTombstone).span, ''); else { const value = (c as StringJsonCrdtChunk).value; - return new StringChunk(id, value.length, value); + return new nodes.StrChunk(id, value.length, value); } }); } @@ -153,9 +145,9 @@ export class Decoder { return rga; } - protected cBin(doc: Model, node: BinaryJsonCrdtNode): BinaryRga { + protected cBin(doc: Model, node: BinaryJsonCrdtNode): nodes.BinNode { const id = this.cTs(node.id); - const rga = new BinaryRga(id); + const rga = new nodes.BinNode(id); const chunks = node.chunks; const length = chunks.length; const self = this; @@ -165,11 +157,11 @@ export class Decoder { const c = chunks[i++]; const id = self.cTs(c.id); if (typeof (c as JsonCrdtRgaTombstone).span === 'number') - return new BinaryChunk(id, (c as JsonCrdtRgaTombstone).span, undefined); + return new nodes.BinChunk(id, (c as JsonCrdtRgaTombstone).span, undefined); else { const value = (c as BinaryJsonCrdtChunk).value; const buf = fromBase64(value); - return new BinaryChunk(id, buf.length, buf); + return new nodes.BinChunk(id, buf.length, buf); } }); } @@ -177,18 +169,18 @@ export class Decoder { return rga; } - protected cVal(doc: Model, node: ValueJsonCrdtNode): ValueLww { + protected cVal(doc: Model, node: ValueJsonCrdtNode): nodes.ValNode { const id = this.cTs(node.id); const val = this.cNode(doc, node.node); - const obj = new ValueLww(doc, id, val.id); + const obj = new nodes.ValNode(doc, id, val.id); doc.index.set(id, obj); return obj; } - protected cConst(doc: Model, node: ConstantJsonCrdtNode): Const { + protected cConst(doc: Model, node: ConstantJsonCrdtNode): nodes.ConNode { const id = this.cTs(node.id); const val = node.timestamp ? this.cTs(node.value as JsonCrdtLogicalTimestamp) : node.value; - const obj = new Const(id, val); + const obj = new nodes.ConNode(id, val); doc.index.set(id, obj); return obj; } diff --git a/src/json-crdt/codec/structural/json/Encoder.ts b/src/json-crdt/codec/structural/json/Encoder.ts index cefa2b3ce4..7a37cbdac0 100644 --- a/src/json-crdt/codec/structural/json/Encoder.ts +++ b/src/json-crdt/codec/structural/json/Encoder.ts @@ -1,11 +1,5 @@ -import {ArrayRga, ArrayChunk} from '../../../types/rga-array/ArrayRga'; -import {BinaryRga, BinaryChunk} from '../../../types/rga-binary/BinaryRga'; -import {Const} from '../../../types/const/Const'; -import {JsonNode} from '../../../types'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {StringRga, StringChunk} from '../../../types/rga-string/StringRga'; +import * as nodes from '../../../nodes'; import {toBase64} from '../../../../util/base64/toBase64'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; import {SESSION} from '../../../../json-crdt-patch/constants'; import {ITimestampStruct, IVectorClock, Timestamp} from '../../../../json-crdt-patch/clock'; import {Model} from '../../../model'; @@ -26,7 +20,6 @@ import type { JsonCrdtSnapshot, TupleJsonCrdtNode, } from './types'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; export class Encoder { protected model!: Model; @@ -54,18 +47,18 @@ export class Encoder { return ts.sid === SESSION.SERVER ? ts.time : [ts.sid, ts.time]; } - public cNode(node: JsonNode): JsonCrdtNode { - if (node instanceof ObjectLww) return this.cObj(node); - else if (node instanceof ArrayRga) return this.cArr(node); - else if (node instanceof StringRga) return this.cStr(node); - else if (node instanceof ValueLww) return this.cVal(node); - else if (node instanceof Const) return this.cConst(node); - else if (node instanceof BinaryRga) return this.cBin(node); - else if (node instanceof ArrayLww) return this.cTup(node); + public cNode(node: nodes.JsonNode): JsonCrdtNode { + if (node instanceof nodes.ObjNode) return this.cObj(node); + else if (node instanceof nodes.ArrNode) return this.cArr(node); + else if (node instanceof nodes.StrNode) return this.cStr(node); + else if (node instanceof nodes.ValNode) return this.cVal(node); + else if (node instanceof nodes.ConNode) return this.cConst(node); + else if (node instanceof nodes.BinNode) return this.cBin(node); + else if (node instanceof nodes.VecNode) return this.cTup(node); throw new Error('UNKNOWN_NODE'); } - public cObj(obj: ObjectLww): ObjectJsonCrdtNode { + public cObj(obj: nodes.ObjNode): ObjectJsonCrdtNode { const keys: Record = {}; obj.nodes((node, key) => { keys[key] = this.cNode(node); @@ -77,7 +70,7 @@ export class Encoder { }; } - public cTup(obj: ArrayLww): TupleJsonCrdtNode { + public cTup(obj: nodes.VecNode): TupleJsonCrdtNode { const components: TupleJsonCrdtNode['components'] = []; const elements = obj.elements; const length = elements.length; @@ -94,7 +87,7 @@ export class Encoder { }; } - public cArr(obj: ArrayRga): ArrayJsonCrdtNode { + public cArr(obj: nodes.ArrNode): ArrayJsonCrdtNode { const chunks: (ArrayJsonCrdtChunk | JsonCrdtRgaTombstone)[] = []; const iterator = obj.iterator(); let chunk; @@ -106,7 +99,7 @@ export class Encoder { }; } - public cArrChunk(chunk: ArrayChunk): ArrayJsonCrdtChunk | JsonCrdtRgaTombstone { + public cArrChunk(chunk: nodes.ArrChunk): ArrayJsonCrdtChunk | JsonCrdtRgaTombstone { if (chunk.del) { const tombstone: JsonCrdtRgaTombstone = { id: this.cTs(chunk.id), @@ -122,11 +115,11 @@ export class Encoder { return res; } - public cStr(obj: StringRga): StringJsonCrdtNode { + public cStr(obj: nodes.StrNode): StringJsonCrdtNode { const chunks: (StringJsonCrdtChunk | JsonCrdtRgaTombstone)[] = []; const iterator = obj.iterator(); let chunk; - while ((chunk = iterator())) chunks.push(this.cStrChunk(chunk as StringChunk)); + while ((chunk = iterator())) chunks.push(this.cStrChunk(chunk as nodes.StrChunk)); return { type: 'str', id: this.cTs(obj.id), @@ -134,7 +127,7 @@ export class Encoder { }; } - public cStrChunk(chunk: StringChunk): StringJsonCrdtChunk | JsonCrdtRgaTombstone { + public cStrChunk(chunk: nodes.StrChunk): StringJsonCrdtChunk | JsonCrdtRgaTombstone { if (chunk.del) { const tombstone: JsonCrdtRgaTombstone = { id: this.cTs(chunk.id), @@ -149,11 +142,11 @@ export class Encoder { return res; } - public cBin(obj: BinaryRga): BinaryJsonCrdtNode { + public cBin(obj: nodes.BinNode): BinaryJsonCrdtNode { const chunks: (BinaryJsonCrdtChunk | JsonCrdtRgaTombstone)[] = []; const iterator = obj.iterator(); let chunk; - while ((chunk = iterator())) chunks.push(this.cBinChunk(chunk as BinaryChunk)); + while ((chunk = iterator())) chunks.push(this.cBinChunk(chunk as nodes.BinChunk)); return { type: 'bin', id: this.cTs(obj.id), @@ -161,7 +154,7 @@ export class Encoder { }; } - public cBinChunk(chunk: BinaryChunk): BinaryJsonCrdtChunk | JsonCrdtRgaTombstone { + public cBinChunk(chunk: nodes.BinChunk): BinaryJsonCrdtChunk | JsonCrdtRgaTombstone { if (chunk.del) { const tombstone: JsonCrdtRgaTombstone = { id: this.cTs(chunk.id), @@ -176,7 +169,7 @@ export class Encoder { return res; } - public cVal(obj: ValueLww): ValueJsonCrdtNode { + public cVal(obj: nodes.ValNode): ValueJsonCrdtNode { return { type: 'val', id: this.cTs(obj.id), @@ -184,7 +177,7 @@ export class Encoder { }; } - public cConst(obj: Const): ConstantJsonCrdtNode { + public cConst(obj: nodes.ConNode): ConstantJsonCrdtNode { const node: ConstantJsonCrdtNode = { type: 'const', id: this.cTs(obj.id), diff --git a/src/json-crdt/extensions/types.ts b/src/json-crdt/extensions/types.ts index 0d32063fc6..270966b3a9 100644 --- a/src/json-crdt/extensions/types.ts +++ b/src/json-crdt/extensions/types.ts @@ -1,7 +1,7 @@ import type {NodeBuilder} from '../../json-crdt-patch/builder/DelayedValueBuilder'; import type {ModelApi} from '../model/api/ModelApi'; import type {NodeApi} from '../model/api/nodes'; -import type {JsonNode} from '../types'; +import type {JsonNode} from '../nodes'; export type ExtensionValue = [type: Uint8Array, data: unknown]; diff --git a/src/json-crdt/hash.ts b/src/json-crdt/hash.ts index 82b4753165..ceacb816d1 100644 --- a/src/json-crdt/hash.ts +++ b/src/json-crdt/hash.ts @@ -1,12 +1,8 @@ import {CONST, updateNum} from '../json-hash'; -import {Const} from './types/const/Const'; -import {ValueLww} from './types/lww-value/ValueLww'; -import {ObjectLww} from './types/lww-object/ObjectLww'; -import {ArrayLww} from './types/lww-array/ArrayLww'; -import {ArrayRga} from './types/rga-array/ArrayRga'; -import {AbstractRga} from './types/rga'; +import {ConNode, ValNode, ObjNode, VecNode, ArrNode} from './nodes'; +import {AbstractRga} from './nodes/rga'; import {last2} from '../util/trees/util2'; -import type {JsonNode} from './types'; +import type {JsonNode} from './nodes'; import type {ITimestampStruct} from '../json-crdt-patch/clock'; import type {Model} from './model'; @@ -32,19 +28,19 @@ export const updateRga = (state: number, node: AbstractRga): number => * @param node JSON CRDT node from which to compute the hash. */ export const updateNode = (state: number, node: JsonNode): number => { - if (node instanceof Const) return updateId(state, node.id); - if (node instanceof ValueLww) { + if (node instanceof ConNode) return updateId(state, node.id); + if (node instanceof ValNode) { const child = node.child(); if (child) state = updateNode(state, child); return updateId(state, node.id); } - if (node instanceof ObjectLww || node instanceof ArrayLww) { + if (node instanceof ObjNode || node instanceof VecNode) { node.children((child) => { state = updateNode(state, child); }); return updateId(state, node.id); } - if (node instanceof ArrayRga) { + if (node instanceof ArrNode) { node.children((child) => { state = updateNode(state, child); }); diff --git a/src/json-crdt/index.ts b/src/json-crdt/index.ts index 47ab0d3ab9..9a53cede7d 100644 --- a/src/json-crdt/index.ts +++ b/src/json-crdt/index.ts @@ -1,3 +1,3 @@ -export * from './types'; +export * from './nodes'; export * from './extensions/types'; export * from './model'; diff --git a/src/json-crdt/json-patch/JsonPatch.ts b/src/json-crdt/json-patch/JsonPatch.ts index 2a1ed9e969..ff892e68fb 100644 --- a/src/json-crdt/json-patch/JsonPatch.ts +++ b/src/json-crdt/json-patch/JsonPatch.ts @@ -1,7 +1,6 @@ -import {ArrayRga} from '../types/rga-array/ArrayRga'; import {deepEqual} from '../../json-equal/deepEqual'; import {isChild, Path} from '../../json-pointer'; -import {ObjectLww} from '../types/lww-object/ObjectLww'; +import {ObjNode, ArrNode} from '../nodes'; import {toPath} from '../../json-pointer/util'; import type {Model} from '../model'; import type { @@ -72,9 +71,9 @@ export class JsonPatch { const objSteps = steps.slice(0, steps.length - 1); const node = this.model.api.find(objSteps); const key = steps[steps.length - 1]; - if (node instanceof ObjectLww) { + if (node instanceof ObjNode) { builder.insObj(node.id, [[String(key), builder.json(op.value)]]); - } else if (node instanceof ArrayRga) { + } else if (node instanceof ArrNode) { const value = builder.json(op.value); if (key === '-') { const length = node.length(); @@ -102,11 +101,11 @@ export class JsonPatch { const objSteps = steps.slice(0, steps.length - 1); const node = this.model.api.find(objSteps); const key = steps[steps.length - 1]; - if (node instanceof ObjectLww) { + if (node instanceof ObjNode) { const stringKey = String(key); if (node.get(stringKey) === undefined) throw new Error('NOT_FOUND'); builder.insObj(node.id, [[stringKey, builder.const(undefined)]]); - } else if (node instanceof ArrayRga) { + } else if (node instanceof ArrNode) { const key = steps[steps.length - 1]; const index = ~~key; if ('' + index !== key) throw new Error('INVALID_INDEX'); @@ -172,9 +171,9 @@ export class JsonPatch { const objSteps = steps.slice(0, steps.length - 1); const node = model.api.find(objSteps); const key = steps[steps.length - 1]; - if (node instanceof ObjectLww) { + if (node instanceof ObjNode) { return node.get(String(key))?.view(); - } else if (node instanceof ArrayRga) { + } else if (node instanceof ArrNode) { const index = ~~key; if ('' + index !== key) throw new Error('INVALID_INDEX'); const arrNode = node.getNode(index); diff --git a/src/json-crdt/model/Model.ts b/src/json-crdt/model/Model.ts index d79a652f04..a663c58138 100644 --- a/src/json-crdt/model/Model.ts +++ b/src/json-crdt/model/Model.ts @@ -1,5 +1,5 @@ import * as operations from '../../json-crdt-patch/operations'; -import {Const} from '../types/const/Const'; +import {ConNode} from '../nodes/con/ConNode'; import {encoder, decoder} from '../codec/structural/binary/shared'; import { ITimestampStruct, @@ -13,15 +13,15 @@ import {JsonCrdtPatchOperation, Patch} from '../../json-crdt-patch/Patch'; import {ModelApi} from './api/ModelApi'; import {ORIGIN, SESSION, SYSTEM_SESSION_TIME} from '../../json-crdt-patch/constants'; import {randomSessionId} from './util'; -import {RootLww, ValueLww, ArrayLww, ObjectLww, StringRga, BinaryRga, ArrayRga, BuilderNodeToJsonNode} from '../types'; +import {RootNode, ValNode, VecNode, ObjNode, StrNode, BinNode, ArrNode, BuilderNodeToJsonNode} from '../nodes'; import {printTree} from '../../util/print/printTree'; import {Extensions} from '../extensions/Extensions'; import {AvlMap} from '../../util/trees/avl/AvlMap'; -import type {JsonNode, JsonNodeView} from '../types/types'; +import type {JsonNode, JsonNodeView} from '../nodes/types'; import type {Printable} from '../../util/print/types'; import type {NodeBuilder} from '../../json-crdt-patch'; -export const UNDEFINED = new Const(ORIGIN, undefined); +export const UNDEFINED = new ConNode(ORIGIN, undefined); /** * In instance of Model class represents the underlying data structure, @@ -75,7 +75,7 @@ export class Model implements Printabl * so that the JSON document does not necessarily need to be an object. The * JSON document can be any JSON value. */ - public root: RootLww = new RootLww(this, ORIGIN); + public root: RootNode = new RootNode(this, ORIGIN); /** * Clock that keeps track of logical timestamps of the current editing session @@ -175,30 +175,30 @@ export class Model implements Printabl const index = this.index; if (op instanceof operations.InsStrOp) { const node = index.get(op.obj); - if (node instanceof StringRga) node.ins(op.ref, op.id, op.data); + if (node instanceof StrNode) node.ins(op.ref, op.id, op.data); } else if (op instanceof operations.NewObjOp) { const id = op.id; - if (!index.get(id)) index.set(id, new ObjectLww(this, id)); + if (!index.get(id)) index.set(id, new ObjNode(this, id)); } else if (op instanceof operations.NewArrOp) { const id = op.id; - if (!index.get(id)) index.set(id, new ArrayRga(this, id)); + if (!index.get(id)) index.set(id, new ArrNode(this, id)); } else if (op instanceof operations.NewStrOp) { const id = op.id; - if (!index.get(id)) index.set(id, new StringRga(id)); + if (!index.get(id)) index.set(id, new StrNode(id)); } else if (op instanceof operations.NewValOp) { const id = op.id; if (!index.get(id)) { const val = index.get(op.val); - if (val) index.set(id, new ValueLww(this, id, op.val)); + if (val) index.set(id, new ValNode(this, id, op.val)); } } else if (op instanceof operations.NewConOp) { const id = op.id; - if (!index.get(id)) index.set(id, new Const(id, op.val)); + if (!index.get(id)) index.set(id, new ConNode(id, op.val)); } else if (op instanceof operations.InsObjOp) { const node = index.get(op.obj); const tuples = op.data; const length = tuples.length; - if (node instanceof ObjectLww) { + if (node instanceof ObjNode) { for (let i = 0; i < length; i++) { const tuple = tuples[i]; const valueNode = index.get(tuple[1]); @@ -212,7 +212,7 @@ export class Model implements Printabl const node = index.get(op.obj); const tuples = op.data; const length = tuples.length; - if (node instanceof ArrayLww) { + if (node instanceof VecNode) { for (let i = 0; i < length; i++) { const tuple = tuples[i]; const valueNode = index.get(tuple[1]); @@ -225,7 +225,7 @@ export class Model implements Printabl } else if (op instanceof operations.InsValOp) { const obj = op.obj; const node = obj.sid === SESSION.SYSTEM && obj.time === SYSTEM_SESSION_TIME.ORIGIN ? this.root : index.get(obj); - if (node instanceof ValueLww) { + if (node instanceof ValNode) { const newValue = index.get(op.val); if (newValue) { const old = node.set(op.val); @@ -234,7 +234,7 @@ export class Model implements Printabl } } else if (op instanceof operations.InsArrOp) { const node = index.get(op.obj); - if (node instanceof ArrayRga) { + if (node instanceof ArrNode) { const nodes: ITimestampStruct[] = []; const data = op.data; const length = data.length; @@ -249,7 +249,7 @@ export class Model implements Printabl } } else if (op instanceof operations.DelOp) { const node = index.get(op.obj); - if (node instanceof ArrayRga) { + if (node instanceof ArrNode) { const length = op.what.length; for (let i = 0; i < length; i++) { const span = op.what[i]; @@ -259,17 +259,17 @@ export class Model implements Printabl } } node.delete(op.what); - } else if (node instanceof StringRga) node.delete(op.what); - else if (node instanceof BinaryRga) node.delete(op.what); + } else if (node instanceof StrNode) node.delete(op.what); + else if (node instanceof BinNode) node.delete(op.what); } else if (op instanceof operations.NewBinOp) { const id = op.id; - if (!index.get(id)) index.set(id, new BinaryRga(id)); + if (!index.get(id)) index.set(id, new BinNode(id)); } else if (op instanceof operations.InsBinOp) { const node = index.get(op.obj); - if (node instanceof BinaryRga) node.ins(op.ref, op.id, op.data); + if (node instanceof BinNode) node.ins(op.ref, op.id, op.data); } else if (op instanceof operations.NewVecOp) { const id = op.id; - if (!index.get(id)) index.set(id, new ArrayLww(this, id)); + if (!index.get(id)) index.set(id, new VecNode(this, id)); } } diff --git a/src/json-crdt/model/__tests__/Model.array.spec.ts b/src/json-crdt/model/__tests__/Model.array.spec.ts index c3e9146b02..41cb2c8f70 100644 --- a/src/json-crdt/model/__tests__/Model.array.spec.ts +++ b/src/json-crdt/model/__tests__/Model.array.spec.ts @@ -1,6 +1,6 @@ import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; import {Model} from '../Model'; -import {ArrayRga} from '../../types/rga-array/ArrayRga'; +import {ArrNode} from '../../nodes'; import {interval, VectorClock, tick} from '../../../json-crdt-patch/clock'; describe('Document', () => { @@ -11,7 +11,7 @@ describe('Document', () => { const arrId = builder.arr(); doc.applyPatch(builder.patch); const obj = doc.index.get(arrId); - expect(obj).toBeInstanceOf(ArrayRga); + expect(obj).toBeInstanceOf(ArrNode); }); test('can set array as document root', () => { @@ -415,7 +415,7 @@ describe('Document', () => { const ins1 = builder.insArr(arr, arr, [f]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; expect(node.find(0)).toStrictEqual(ins1); }); @@ -426,7 +426,7 @@ describe('Document', () => { const ins1 = builder.insArr(arr, arr, [builder.const(false), builder.const(true), builder.const(true)]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; expect(node.find(0)).toStrictEqual(ins1); expect(node.find(1)).toStrictEqual(tick(ins1, 1)); expect(node.find(2)).toStrictEqual(tick(ins1, 2)); @@ -444,7 +444,7 @@ describe('Document', () => { const ins3 = builder.insArr(arr, tick(ins2, 2), [f, t, t]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; expect(node.find(0)).toStrictEqual(ins1); expect(node.find(1)).toStrictEqual(tick(ins1, 1)); expect(node.find(2)).toStrictEqual(tick(ins1, 2)); @@ -469,7 +469,7 @@ describe('Document', () => { const ins3 = builder.insArr(arr, tick(ins2, 2), [f, t, t]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; expect(node.getNode(0)!.id).toStrictEqual(f); expect(node.getNode(1)!.id).toStrictEqual(t); expect(node.getNode(2)!.id).toStrictEqual(t); @@ -492,7 +492,7 @@ describe('Document', () => { const ins1 = builder.insArr(arr, arr, [f, t, t, n]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(1, 2); expect(span[0].sid).toBe(ins1.sid); expect(span[0].time).toBe(ins1.time + 1); @@ -509,7 +509,7 @@ describe('Document', () => { const ins1 = builder.insArr(arr, arr, [f, t, t, n]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(0, 2); expect(span[0].sid).toBe(ins1.sid); expect(span[0].time).toBe(ins1.time); @@ -526,7 +526,7 @@ describe('Document', () => { const ins1 = builder.insArr(arr, arr, [f, t, t, n]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(2, 2); expect(span[0].sid).toBe(ins1.sid); expect(span[0].time).toBe(ins1.time + 2); @@ -545,7 +545,7 @@ describe('Document', () => { const ins2 = builder.insArr(arr, tick(ins1, 3), [t, t, t]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(2, 3); expect(span.length).toBe(2); expect(span[0].sid).toBe(ins1.sid); @@ -570,7 +570,7 @@ describe('Document', () => { const ins3 = builder.insArr(arr, ins2, [t, t]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(2, 5); expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -598,7 +598,7 @@ describe('Document', () => { const ins3 = builder.insArr(arr, ins2, [t, t]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(2, 4); expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -626,7 +626,7 @@ describe('Document', () => { const ins3 = builder.insArr(arr, ins2, [t, t]); builder.root(arr); doc.applyPatch(builder.patch); - const node = doc.index.get(arr)! as ArrayRga; + const node = doc.index.get(arr)! as ArrNode; const span = node.findInterval(2, 3); expect(span.length).toBe(2); expect(span[0].sid).toBe(ins1.sid); diff --git a/src/json-crdt/model/__tests__/Model.binary.spec.ts b/src/json-crdt/model/__tests__/Model.binary.spec.ts index 7ec1da7b08..f1b184f5f8 100644 --- a/src/json-crdt/model/__tests__/Model.binary.spec.ts +++ b/src/json-crdt/model/__tests__/Model.binary.spec.ts @@ -1,4 +1,4 @@ -import {BinaryRga} from '../../types/rga-binary/BinaryRga'; +import {BinNode} from '../../nodes'; import {Model} from '../Model'; import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; import {interval, tick} from '../../../json-crdt-patch/clock'; @@ -11,7 +11,7 @@ describe('Document', () => { const id = builder.bin(); doc.applyPatch(builder.patch); const obj = doc.index.get(id); - expect(obj).toBeInstanceOf(BinaryRga); + expect(obj).toBeInstanceOf(BinNode); }); test('can set binary as document root', () => { @@ -261,7 +261,7 @@ describe('Document', () => { const ins1 = builder.insBin(id, id, new Uint8Array(4)); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; expect(node.find(0)!).toStrictEqual(ins1); }); @@ -272,7 +272,7 @@ describe('Document', () => { const ins1 = builder.insBin(id, id, new Uint8Array([1, 2, 3, 4, 5])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; expect(node.find(2)!).toStrictEqual(tick(ins1, 2)); }); @@ -284,7 +284,7 @@ describe('Document', () => { const ins2 = builder.insBin(id, tick(ins1, 4), new Uint8Array([6, 7, 8, 9, 10, 11])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; // console.log(doc.toJson()); expect(node.find(2)!).toStrictEqual(tick(ins1, 2)); expect(node.find(6)!).toStrictEqual(tick(ins2, 1)); @@ -299,7 +299,7 @@ describe('Document', () => { const ins1 = builder.insBin(id, id, new Uint8Array([1, 2, 3])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(1, 1)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -314,7 +314,7 @@ describe('Document', () => { const ins1 = builder.insBin(id, id, new Uint8Array([1, 2, 3, 4, 5])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(2, 2)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -329,7 +329,7 @@ describe('Document', () => { const ins1 = builder.insBin(id, id, new Uint8Array([1, 2, 3, 4, 5])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(0, 3)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -344,7 +344,7 @@ describe('Document', () => { const ins1 = builder.insBin(id, id, new Uint8Array([1, 2, 3, 4, 5])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(2, 3)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -361,7 +361,7 @@ describe('Document', () => { const ins2 = builder.insBin(id, tick(ins1, 2), new Uint8Array([4, 5, 6])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(2, 2)!; expect(span.length).toBe(2); expect(span[0].sid).toBe(ins1.sid); @@ -383,7 +383,7 @@ describe('Document', () => { const ins3 = builder.insBin(id, tick(ins2, 2), new Uint8Array([7, 8, 9])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(0, 9)!; expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -408,7 +408,7 @@ describe('Document', () => { const ins3 = builder.insBin(id, tick(ins2, 2), new Uint8Array([7, 8, 9])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(1, 7)!; expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -433,7 +433,7 @@ describe('Document', () => { const ins3 = builder.insBin(id, tick(ins2, 2), new Uint8Array([7, 8, 9])); builder.root(id); doc.applyPatch(builder.patch); - const node = doc.index.get(id)! as BinaryRga; + const node = doc.index.get(id)! as BinNode; const span = node.findInterval(2, 5)!; expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); diff --git a/src/json-crdt/model/__tests__/Model.node-deletion.spec.ts b/src/json-crdt/model/__tests__/Model.node-deletion.spec.ts index d885300ec2..45285a71fa 100644 --- a/src/json-crdt/model/__tests__/Model.node-deletion.spec.ts +++ b/src/json-crdt/model/__tests__/Model.node-deletion.spec.ts @@ -1,4 +1,4 @@ -import {ValueLww} from '../../types/lww-value/ValueLww'; +import {ValNode} from '../../nodes'; import {Model} from '../Model'; test('removes from index rewritten root nodes', () => { @@ -34,11 +34,11 @@ test('removes from index deleted object keys', () => { expect(!!doc.index.get(keyValue)).toBe(false); }); -test('removes from index rewritten ValueLww register values', () => { +test('removes from index rewritten ValNode register values', () => { const doc = Model.withLogicalClock(); doc.api.root([123]); const register = doc.api.val([0]).node; - expect(register).toBeInstanceOf(ValueLww); + expect(register).toBeInstanceOf(ValNode); const oldValue = register.val; expect(!!doc.index.get(oldValue)).toBe(true); doc.api.val([0]).set(456); diff --git a/src/json-crdt/model/__tests__/Model.string.spec.ts b/src/json-crdt/model/__tests__/Model.string.spec.ts index 957843f158..e43a4eda55 100644 --- a/src/json-crdt/model/__tests__/Model.string.spec.ts +++ b/src/json-crdt/model/__tests__/Model.string.spec.ts @@ -1,6 +1,6 @@ import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; import {Model} from '../Model'; -import {StringRga} from '../../types/rga-string/StringRga'; +import {StrNode} from '../../nodes'; import {interval, tick} from '../../../json-crdt-patch/clock'; describe('Document', () => { @@ -11,7 +11,7 @@ describe('Document', () => { const str = builder.str(); doc.applyPatch(builder.patch); const obj = doc.index.get(str); - expect(obj).toBeInstanceOf(StringRga); + expect(obj).toBeInstanceOf(StrNode); }); test('can set string as document root', () => { @@ -261,7 +261,7 @@ describe('Document', () => { const ins1 = builder.insStr(str, str, 'H'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; expect(node.find(0)!.sid).toBe(ins1.sid); expect(node.find(0)!.time).toBe(ins1.time); }); @@ -273,7 +273,7 @@ describe('Document', () => { const ins1 = builder.insStr(str, str, 'Hello'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; expect(node.find(2)!.sid).toBe(tick(ins1, 2).sid); expect(node.find(2)!.time).toBe(tick(ins1, 2).time); }); @@ -286,7 +286,7 @@ describe('Document', () => { const ins2 = builder.insStr(str, tick(ins1, 4), ' world'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; // console.log(doc.toJson()); expect(node.find(2)!.sid).toBe(tick(ins1, 2).sid); expect(node.find(2)!.time).toBe(tick(ins1, 2).time); @@ -304,7 +304,7 @@ describe('Document', () => { const ins1 = builder.insStr(str, str, 'abc'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(1, 1)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -319,7 +319,7 @@ describe('Document', () => { const ins1 = builder.insStr(str, str, 'abcde'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(2, 2)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -334,7 +334,7 @@ describe('Document', () => { const ins1 = builder.insStr(str, str, 'abcde'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(0, 3)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -349,7 +349,7 @@ describe('Document', () => { const ins1 = builder.insStr(str, str, 'abcde'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(2, 3)!; expect(span.length).toBe(1); expect(span[0].sid).toBe(ins1.sid); @@ -366,7 +366,7 @@ describe('Document', () => { const ins2 = builder.insStr(str, tick(ins1, 2), 'def'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(2, 2)!; expect(span.length).toBe(2); expect(span[0].sid).toBe(ins1.sid); @@ -388,7 +388,7 @@ describe('Document', () => { const ins3 = builder.insStr(str, tick(ins2, 2), 'ghi'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(0, 9)!; expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -413,7 +413,7 @@ describe('Document', () => { const ins3 = builder.insStr(str, tick(ins2, 2), 'ghi'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(1, 7)!; expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -438,7 +438,7 @@ describe('Document', () => { const ins3 = builder.insStr(str, tick(ins2, 2), 'ghi'); builder.root(str); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; const span = node.findInterval(2, 5)!; expect(span.length).toBe(3); expect(span[0].sid).toBe(ins1.sid); @@ -463,7 +463,7 @@ describe('Document', () => { doc.applyPatch(builder.patch); builder.insStr(str, ins2, 'c'); doc.applyPatch(builder.patch); - const node = doc.index.get(str)! as StringRga; + const node = doc.index.get(str)! as StrNode; expect(node.size()).toBe(1); }); }); diff --git a/src/json-crdt/model/__tests__/Model.tuple.spec.ts b/src/json-crdt/model/__tests__/Model.tuple.spec.ts index e04bdf4c29..5eddc65850 100644 --- a/src/json-crdt/model/__tests__/Model.tuple.spec.ts +++ b/src/json-crdt/model/__tests__/Model.tuple.spec.ts @@ -1,6 +1,6 @@ import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; import {Model} from '../Model'; -import {ArrayLww} from '../../types/lww-array/ArrayLww'; +import {VecNode} from '../../nodes'; describe('Document', () => { describe('tuple', () => { @@ -10,7 +10,7 @@ describe('Document', () => { const id = builder.vec(); doc.applyPatch(builder.patch); const obj = doc.index.get(id); - expect(obj).toBeInstanceOf(ArrayLww); + expect(obj).toBeInstanceOf(VecNode); expect(obj?.view()).toStrictEqual([]); }); diff --git a/src/json-crdt/model/__tests__/Model.types.spec.ts b/src/json-crdt/model/__tests__/Model.types.spec.ts index 37595c03e2..5d9ce0170a 100644 --- a/src/json-crdt/model/__tests__/Model.types.spec.ts +++ b/src/json-crdt/model/__tests__/Model.types.spec.ts @@ -1,13 +1,11 @@ import {Model} from '../Model'; -import type {ObjectLww} from '../../types/lww-object/ObjectLww'; -import type {StringRga} from '../../types/rga-string/StringRga'; -import type {Const} from '../../types/const/Const'; +import type {ConNode, ObjNode, StrNode} from '../../nodes'; test('can add TypeScript types to Model view', () => { const model = Model.withLogicalClock() as Model< - ObjectLww<{ - foo: StringRga; - bar: Const; + ObjNode<{ + foo: StrNode; + bar: ConNode; }> >; model.api.root({ diff --git a/src/json-crdt/model/__tests__/Model.value.spec.ts b/src/json-crdt/model/__tests__/Model.value.spec.ts index 17787f2e50..6029e768da 100644 --- a/src/json-crdt/model/__tests__/Model.value.spec.ts +++ b/src/json-crdt/model/__tests__/Model.value.spec.ts @@ -1,6 +1,6 @@ import {Model} from '../Model'; import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; -import {ValueLww} from '../../types/lww-value/ValueLww'; +import {ValNode} from '../../nodes'; describe('Document', () => { describe('value', () => { @@ -11,7 +11,7 @@ describe('Document', () => { const numId = builder.val(val); doc.applyPatch(builder.patch); const obj = doc.index.get(numId); - expect(obj).toBeInstanceOf(ValueLww); + expect(obj).toBeInstanceOf(ValNode); }); test('can set value as document root', () => { diff --git a/src/json-crdt/model/api/ModelApi.ts b/src/json-crdt/model/api/ModelApi.ts index cbf6b97bcf..97f25c93f5 100644 --- a/src/json-crdt/model/api/ModelApi.ts +++ b/src/json-crdt/model/api/ModelApi.ts @@ -1,9 +1,9 @@ -import {ArrayLww, Const, ObjectLww, ArrayRga, BinaryRga, StringRga, ValueLww} from '../../types'; -import {ApiPath, ArrayApi, BinaryApi, ConstApi, NodeApi, ObjectApi, StringApi, VectorApi, ValueApi} from './nodes'; +import {VecNode, ConNode, ObjNode, ArrNode, BinNode, StrNode, ValNode} from '../../nodes'; +import {ApiPath, ArrApi, BinApi, ConApi, NodeApi, ObjApi, StrApi, VecApi, ValApi} from './nodes'; import {Emitter} from '../../../util/events/Emitter'; import {Patch} from '../../../json-crdt-patch/Patch'; import {PatchBuilder} from '../../../json-crdt-patch/PatchBuilder'; -import type {JsonNode} from '../../types'; +import type {JsonNode} from '../../nodes'; import type {Model} from '../Model'; /** @@ -65,22 +65,22 @@ export class ModelApi { * Returns a local change API for the given node. If an instance already * exists, returns the existing instance. */ - public wrap(node: ValueLww): ValueApi; - public wrap(node: StringRga): StringApi; - public wrap(node: BinaryRga): BinaryApi; - public wrap(node: ArrayRga): ArrayApi; - public wrap(node: ObjectLww): ObjectApi; - public wrap(node: Const): ConstApi; - public wrap(node: ArrayLww): VectorApi; + public wrap(node: ValNode): ValApi; + public wrap(node: StrNode): StrApi; + public wrap(node: BinNode): BinApi; + public wrap(node: ArrNode): ArrApi; + public wrap(node: ObjNode): ObjApi; + public wrap(node: ConNode): ConApi; + public wrap(node: VecNode): VecApi; public wrap(node: JsonNode): NodeApi; public wrap(node: JsonNode) { - if (node instanceof ValueLww) return node.api || (node.api = new ValueApi(node, this)); - else if (node instanceof StringRga) return node.api || (node.api = new StringApi(node, this)); - else if (node instanceof BinaryRga) return node.api || (node.api = new BinaryApi(node, this)); - else if (node instanceof ArrayRga) return node.api || (node.api = new ArrayApi(node, this)); - else if (node instanceof ObjectLww) return node.api || (node.api = new ObjectApi(node, this)); - else if (node instanceof Const) return node.api || (node.api = new ConstApi(node, this)); - else if (node instanceof ArrayLww) return node.api || (node.api = new VectorApi(node, this)); + if (node instanceof ValNode) return node.api || (node.api = new ValApi(node, this)); + else if (node instanceof StrNode) return node.api || (node.api = new StrApi(node, this)); + else if (node instanceof BinNode) return node.api || (node.api = new BinApi(node, this)); + else if (node instanceof ArrNode) return node.api || (node.api = new ArrApi(node, this)); + else if (node instanceof ObjNode) return node.api || (node.api = new ObjApi(node, this)); + else if (node instanceof ConNode) return node.api || (node.api = new ConApi(node, this)); + else if (node instanceof VecNode) return node.api || (node.api = new VecApi(node, this)); else throw new Error('UNKNOWN_NODE'); } @@ -88,7 +88,7 @@ export class ModelApi { * Local changes API for the root node. */ public get r() { - return new ValueApi(this.model.root, this); + return new ValApi(this.model.root, this); } /** @ignore */ diff --git a/src/json-crdt/model/api/__tests__/ModelApi.proxy.spec.ts b/src/json-crdt/model/api/__tests__/ModelApi.proxy.spec.ts index 1d12527621..8e85f0870f 100644 --- a/src/json-crdt/model/api/__tests__/ModelApi.proxy.spec.ts +++ b/src/json-crdt/model/api/__tests__/ModelApi.proxy.spec.ts @@ -1,17 +1,13 @@ import {Model} from '../../Model'; -import {ConstApi, ObjectApi, StringApi, VectorApi, ValueApi} from '../nodes'; -import {RootLww} from '../../../types/lww-root/RootLww'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {StringRga} from '../../../types/rga-string/StringRga'; -import {Const} from '../../../types/const/Const'; -import {ArrayLww} from '../../../types/lww-array/ArrayLww'; +import {ConApi, ObjApi, StrApi, VecApi, ValApi} from '../nodes'; +import {ConNode, RootNode, VecNode, ObjNode, StrNode} from '../../../nodes'; import {vec} from '../../../../json-crdt-patch'; test('proxy API supports object types', () => { const model = Model.withLogicalClock() as Model< - ObjectLww<{ - foo: StringRga; - bar: Const; + ObjNode<{ + foo: StrNode; + bar: ConNode; }> >; model.api.root({ @@ -19,40 +15,40 @@ test('proxy API supports object types', () => { bar: 1234, }); const root = model.api.r.proxy(); - const rootApi: ValueApi = root.toApi(); - expect(rootApi).toBeInstanceOf(ValueApi); - expect(rootApi.node).toBeInstanceOf(RootLww); + const rootApi: ValApi = root.toApi(); + expect(rootApi).toBeInstanceOf(ValApi); + expect(rootApi.node).toBeInstanceOf(RootNode); expect(rootApi.view()).toStrictEqual({ foo: 'asdf', bar: 1234, }); const obj = root.val; - const objApi: ObjectApi = obj.toApi(); - expect(objApi).toBeInstanceOf(ObjectApi); - expect(objApi.node).toBeInstanceOf(ObjectLww); + const objApi: ObjApi = obj.toApi(); + expect(objApi).toBeInstanceOf(ObjApi); + expect(objApi.node).toBeInstanceOf(ObjNode); expect(objApi.view()).toStrictEqual({ foo: 'asdf', bar: 1234, }); const foo = obj.foo; - const fooApi: StringApi = foo.toApi(); - expect(fooApi).toBeInstanceOf(StringApi); - expect(fooApi.node).toBeInstanceOf(StringRga); + const fooApi: StrApi = foo.toApi(); + expect(fooApi).toBeInstanceOf(StrApi); + expect(fooApi.node).toBeInstanceOf(StrNode); expect(fooApi.view()).toStrictEqual('asdf'); const bar = obj.bar; - const barApi: ConstApi = bar.toApi(); - expect(barApi).toBeInstanceOf(ConstApi); - expect(barApi.node).toBeInstanceOf(Const); + const barApi: ConApi = bar.toApi(); + expect(barApi).toBeInstanceOf(ConApi); + expect(barApi.node).toBeInstanceOf(ConNode); expect(barApi.view()).toStrictEqual(1234); }); describe('supports all node types', () => { - type Schema = ObjectLww<{ - obj: ObjectLww<{ - str: StringRga; - num: Const; + type Schema = ObjNode<{ + obj: ObjNode<{ + str: StrNode; + num: ConNode; }>; - vec: ArrayLww<[StringRga]>; + vec: VecNode<[StrNode]>; }>; const model = Model.withLogicalClock() as Model; const data = { @@ -69,9 +65,9 @@ describe('supports all node types', () => { test('object as root node', () => { const proxy = model.api.r.proxy(); const obj = proxy.val; - const objApi: ObjectApi = obj.toApi(); - expect(objApi).toBeInstanceOf(ObjectApi); - expect(objApi.node).toBeInstanceOf(ObjectLww); + const objApi: ObjApi = obj.toApi(); + expect(objApi).toBeInstanceOf(ObjApi); + expect(objApi.node).toBeInstanceOf(ObjNode); const keys = new Set(Object.keys(objApi.view())); expect(keys.has('obj')).toBe(true); expect(keys.has('vec')).toBe(true); @@ -80,9 +76,9 @@ describe('supports all node types', () => { test('nested object', () => { const proxy = model.api.r.proxy(); const obj = proxy.val.obj; - const objApi: ObjectApi = obj.toApi(); - expect(objApi).toBeInstanceOf(ObjectApi); - expect(objApi.node).toBeInstanceOf(ObjectLww); + const objApi: ObjApi = obj.toApi(); + expect(objApi).toBeInstanceOf(ObjApi); + expect(objApi.node).toBeInstanceOf(ObjNode); const keys = new Set(Object.keys(objApi.view())); expect(keys.has('str')).toBe(true); expect(keys.has('num')).toBe(true); @@ -91,27 +87,27 @@ describe('supports all node types', () => { test('string as object key', () => { const proxy = model.api.r.proxy(); const str = proxy.val.obj.str; - const strApi: StringApi = str.toApi(); - expect(strApi).toBeInstanceOf(StringApi); - expect(strApi.node).toBeInstanceOf(StringRga); + const strApi: StrApi = str.toApi(); + expect(strApi).toBeInstanceOf(StrApi); + expect(strApi.node).toBeInstanceOf(StrNode); expect(strApi.view()).toStrictEqual('asdf'); }); test('number constant as object key', () => { const proxy = model.api.r.proxy(); const num = proxy.val.obj.num; - const numApi: ConstApi = num.toApi(); - expect(numApi).toBeInstanceOf(ConstApi); - expect(numApi.node).toBeInstanceOf(Const); + const numApi: ConApi = num.toApi(); + expect(numApi).toBeInstanceOf(ConApi); + expect(numApi.node).toBeInstanceOf(ConNode); expect(numApi.view()).toStrictEqual(1234); }); test('vector', () => { const proxy = model.api.r.proxy(); const vec = proxy.val.vec; - const vecApi: VectorApi = vec.toApi(); - expect(vecApi).toBeInstanceOf(VectorApi); - expect(vecApi.node).toBeInstanceOf(ArrayLww); + const vecApi: VecApi = vec.toApi(); + expect(vecApi).toBeInstanceOf(VecApi); + expect(vecApi.node).toBeInstanceOf(VecNode); expect(vecApi.view()).toStrictEqual(['asdf', 1234, true, null]); }); }); diff --git a/src/json-crdt/model/api/__tests__/in.spec.ts b/src/json-crdt/model/api/__tests__/in.spec.ts index 8996403932..6933e16cf7 100644 --- a/src/json-crdt/model/api/__tests__/in.spec.ts +++ b/src/json-crdt/model/api/__tests__/in.spec.ts @@ -1,16 +1,13 @@ -import {Const} from '../../../types/const/Const'; -import {ConstApi, ObjectApi, ValueApi} from '../nodes'; +import {ConNode, RootNode, ValNode, ObjNode} from '../../../nodes'; +import {ConApi, ObjApi, ValApi} from '../nodes'; import {Model} from '../../Model'; -import {ObjectLww} from '../../../types/lww-object/ObjectLww'; -import {RootLww} from '../../../types/lww-root/RootLww'; -import {ValueLww} from '../../../types/lww-value/ValueLww'; describe('can use .in() method to reference any model node', () => { const doc = Model.withLogicalClock(); test('can access root node', () => { const node = doc.api.r.asVal(); - expect(node.node).toBeInstanceOf(RootLww); + expect(node.node).toBeInstanceOf(RootNode); }); doc.api.root({ @@ -18,31 +15,31 @@ describe('can use .in() method to reference any model node', () => { }); doc.api.r.in('/foo/0').asVal().set({bar: 'baz'}); - test('can access array element ValueLww and its contents', () => { + test('can access array element ValNode and its contents', () => { const register1 = doc.api.r.in().in('foo').in(0).asVal(); const register2 = doc.api.val('/foo/0'); const register3 = doc.api.val(['foo', 0]); - expect(register1).toBeInstanceOf(ValueApi); - expect(register1.node).toBeInstanceOf(ValueLww); + expect(register1).toBeInstanceOf(ValApi); + expect(register1.node).toBeInstanceOf(ValNode); expect(register1.node).toBe(register2.node); expect(register1.node).toBe(register3.node); const obj1 = register1.in(); - expect(obj1).toBeInstanceOf(ObjectApi); - expect(obj1.node).toBeInstanceOf(ObjectLww); + expect(obj1).toBeInstanceOf(ObjApi); + expect(obj1.node).toBeInstanceOf(ObjNode); }); doc.api.obj([]).set({val: doc.api.builder.jsonVal(123)}); - test('can access object key ValueLww and its contents', () => { + test('can access object key ValNode and its contents', () => { const register1 = doc.api.r.in().in('val').asVal(); const register2 = doc.api.val('/val'); const register3 = doc.api.val(['val']); - expect(register1).toBeInstanceOf(ValueApi); - expect(register1.node).toBeInstanceOf(ValueLww); + expect(register1).toBeInstanceOf(ValApi); + expect(register1.node).toBeInstanceOf(ValNode); expect(register1.node).toBe(register2.node); expect(register1.node).toBe(register3.node); const const1 = register1.in(); - expect(const1).toBeInstanceOf(ConstApi); - expect(const1.node).toBeInstanceOf(Const); + expect(const1).toBeInstanceOf(ConApi); + expect(const1.node).toBeInstanceOf(ConNode); }); }); diff --git a/src/json-crdt/model/api/__tests__/scenarios/obect-in-value-in-array.spec.ts b/src/json-crdt/model/api/__tests__/scenarios/obect-in-value-in-array.spec.ts index 949cbfa90c..04d1ac4965 100644 --- a/src/json-crdt/model/api/__tests__/scenarios/obect-in-value-in-array.spec.ts +++ b/src/json-crdt/model/api/__tests__/scenarios/obect-in-value-in-array.spec.ts @@ -1,6 +1,6 @@ import {Model} from '../../../Model'; -test('handles ObjectLww inside ValueLww, which was set on ArrayRga', () => { +test('handles ObjNode inside ValNode, which was set on ArrNode', () => { const doc = Model.withLogicalClock(); doc.api.root([123]); doc.api.val('/0').set({ diff --git a/src/json-crdt/model/api/find.ts b/src/json-crdt/model/api/find.ts index cf0213308d..f77720ef83 100644 --- a/src/json-crdt/model/api/find.ts +++ b/src/json-crdt/model/api/find.ts @@ -1,8 +1,6 @@ -import {ArrayRga} from '../../types/rga-array/ArrayRga'; -import {ObjectLww} from '../../types/lww-object/ObjectLww'; import {Path, toPath} from '../../../json-pointer'; -import {ArrayLww} from '../../types/lww-array/ArrayLww'; -import type {JsonNode} from '../../types'; +import {VecNode, ObjNode, ArrNode} from '../../nodes'; +import type {JsonNode} from '../../nodes'; export const find = (startNode: JsonNode, path: string | Path): JsonNode => { const steps = toPath(path); @@ -14,15 +12,15 @@ export const find = (startNode: JsonNode, path: string | Path): JsonNode => { const step = steps[i++]; node = node.container(); if (!node) throw new Error('NOT_CONTAINER'); - if (node instanceof ObjectLww) { + if (node instanceof ObjNode) { const nextNode = node.get(String(step)) as JsonNode | undefined; if (!nextNode) throw new Error('NOT_FOUND'); node = nextNode; - } else if (node instanceof ArrayRga) { + } else if (node instanceof ArrNode) { const nextNode = node.getNode(Number(step)); if (!nextNode) throw new Error('NOT_FOUND'); node = nextNode; - } else if (node instanceof ArrayLww) { + } else if (node instanceof VecNode) { const nextNode = node.get(Number(step)) as JsonNode | undefined; if (!nextNode) throw new Error('NOT_FOUND'); node = nextNode; diff --git a/src/json-crdt/model/api/nodes.ts b/src/json-crdt/model/api/nodes.ts index 21684afc3d..b24b091373 100644 --- a/src/json-crdt/model/api/nodes.ts +++ b/src/json-crdt/model/api/nodes.ts @@ -1,18 +1,12 @@ -import {ArrayRga} from '../../types/rga-array/ArrayRga'; -import {BinaryRga} from '../../types/rga-binary/BinaryRga'; -import {Const} from '../../types/const/Const'; import {find} from './find'; import {ITimestampStruct, Timestamp} from '../../../json-crdt-patch/clock'; -import {ObjectLww} from '../../types/lww-object/ObjectLww'; import {Path} from '../../../json-pointer'; -import {StringRga} from '../../types/rga-string/StringRga'; -import {ValueLww} from '../../types/lww-value/ValueLww'; -import {ArrayLww} from '../../types/lww-array/ArrayLww'; +import {ObjNode, ArrNode, BinNode, ConNode, VecNode, ValNode, StrNode} from '../../nodes'; import {ExtensionApi, ExtensionDefinition, ExtensionJsonNode} from '../../extensions/types'; import {NodeEvents} from './events/NodeEvents'; import {printTree} from '../../../util/print/printTree'; +import type {JsonNode, JsonNodeView} from '../../nodes'; import type * as types from '../proxy/types'; -import type {JsonNode, JsonNodeView} from '../../types'; import type {ModelApi} from './ModelApi'; import type {Printable} from '../../../util/print/types'; import type {JsonNodeApi} from './types'; @@ -78,38 +72,38 @@ export class NodeApi implements Printable { return this.api.wrap(node as any); } - public asVal(): ValueApi { - if (this.node instanceof ValueLww) return this.api.wrap(this.node as ValueLww); + public asVal(): ValApi { + if (this.node instanceof ValNode) return this.api.wrap(this.node as ValNode); throw new Error('NOT_VAL'); } - public asStr(): StringApi { - if (this.node instanceof StringRga) return this.api.wrap(this.node); + public asStr(): StrApi { + if (this.node instanceof StrNode) return this.api.wrap(this.node); throw new Error('NOT_STR'); } - public asBin(): BinaryApi { - if (this.node instanceof BinaryRga) return this.api.wrap(this.node); + public asBin(): BinApi { + if (this.node instanceof BinNode) return this.api.wrap(this.node); throw new Error('NOT_BIN'); } - public asArr(): ArrayApi { - if (this.node instanceof ArrayRga) return this.api.wrap(this.node); + public asArr(): ArrApi { + if (this.node instanceof ArrNode) return this.api.wrap(this.node); throw new Error('NOT_ARR'); } - public asTup(): VectorApi { - if (this.node instanceof ArrayLww) return this.api.wrap(this.node as ArrayLww); + public asTup(): VecApi { + if (this.node instanceof VecNode) return this.api.wrap(this.node as VecNode); throw new Error('NOT_ARR'); } - public asObj(): ObjectApi { - if (this.node instanceof ObjectLww) return this.api.wrap(this.node as ObjectLww); + public asObj(): ObjApi { + if (this.node instanceof ObjNode) return this.api.wrap(this.node as ObjNode); throw new Error('NOT_OBJ'); } - public asConst(): ConstApi { - if (this.node instanceof Const) return this.api.wrap(this.node); + public asCon(): ConApi { + if (this.node instanceof ConNode) return this.api.wrap(this.node); throw new Error('NOT_CONST'); } @@ -124,32 +118,32 @@ export class NodeApi implements Printable { throw new Error('NOT_EXT'); } - public val(path?: ApiPath): ValueApi { + public val(path?: ApiPath): ValApi { return this.in(path).asVal(); } - public str(path?: ApiPath): StringApi { + public str(path?: ApiPath): StrApi { return this.in(path).asStr(); } - public bin(path?: ApiPath): BinaryApi { + public bin(path?: ApiPath): BinApi { return this.in(path).asBin(); } - public arr(path?: ApiPath): ArrayApi { + public arr(path?: ApiPath): ArrApi { return this.in(path).asArr(); } - public tup(path?: ApiPath): VectorApi { + public tup(path?: ApiPath): VecApi { return this.in(path).asTup(); } - public obj(path?: ApiPath): ObjectApi { + public obj(path?: ApiPath): ObjApi { return this.in(path).asObj(); } - public const(path?: ApiPath): ConstApi { - return this.in(path).asConst(); + public const(path?: ApiPath): ConApi { + return this.in(path).asCon(); } public view(): JsonNodeView { @@ -162,15 +156,15 @@ export class NodeApi implements Printable { } /** - * Represents the local changes API for the `con` JSON CRDT node {@link Const}. + * Represents the local changes API for the `con` JSON CRDT node {@link ConNode}. * * @category Local API */ -export class ConstApi = Const> extends NodeApi { +export class ConApi = ConNode> extends NodeApi { /** * Returns a proxy object for this node. */ - public proxy(): types.ProxyNodeConst { + public proxy(): types.ProxyNodeCon { return { toApi: () => this, }; @@ -178,16 +172,16 @@ export class ConstApi = Const> extends NodeApi { } /** - * Local changes API for the `val` JSON CRDT node {@link ValueLww}. + * Local changes API for the `val` JSON CRDT node {@link ValNode}. * * @category Local API */ -export class ValueApi = ValueLww> extends NodeApi { +export class ValApi = ValNode> extends NodeApi { /** * Get API instance of the inner node. * @returns Inner node API. */ - public get(): JsonNodeApi ? T : JsonNode> { + public get(): JsonNodeApi ? T : JsonNode> { return this.in() as any; } @@ -223,22 +217,21 @@ export class ValueApi = ValueLww> extends NodeApi = N extends ArrayLww ? T : never; +type UnVecNode = N extends VecNode ? T : never; /** - * Local changes API for the `vec` JSON CRDT node {@link ArrayLww}. + * Local changes API for the `vec` JSON CRDT node {@link VecNode}. * * @category Local API - * @todo Rename to VectorApi. */ -export class VectorApi = ArrayLww> extends NodeApi { +export class VecApi = VecNode> extends NodeApi { /** * Get API instance of a child node. * * @param key Object key to get. * @returns A specified child node API. */ - public get>(key: K): JsonNodeApi[K]> { + public get>(key: K): JsonNodeApi[K]> { return this.in(key as string) as any; } @@ -281,21 +274,21 @@ export class VectorApi = ArrayLww> extends NodeApi< } } -type ObjectLwwNodes = N extends ObjectLww ? T : never; +type UnObjNode = N extends ObjNode ? T : never; /** - * Local changes API for the `obj` JSON CRDT node {@link ObjectLww}. + * Local changes API for the `obj` JSON CRDT node {@link ObjNode}. * * @category Local API */ -export class ObjectApi = ObjectLww> extends NodeApi { +export class ObjApi = ObjNode> extends NodeApi { /** * Get API instance of a child node. * * @param key Object key to get. * @returns A specified child node API. */ - public get>(key: K): JsonNodeApi[K]> { + public get>(key: K): JsonNodeApi[K]> { return this.in(key as string) as any; } @@ -356,13 +349,13 @@ export class ObjectApi = ObjectLww> extends NodeAp } /** - * Local changes API for the `str` JSON CRDT node {@link StringRga}. This API + * Local changes API for the `str` JSON CRDT node {@link StrNode}. This API * allows to insert and delete bytes in the UTF-16 string by referencing its * local character positions. * * @category Local API */ -export class StringApi extends NodeApi { +export class StrApi extends NodeApi { /** * Inserts text at a given position. * @@ -413,13 +406,13 @@ export class StringApi extends NodeApi { } /** - * Local changes API for the `bin` JSON CRDT node {@link BinaryRga}. This API + * Local changes API for the `bin` JSON CRDT node {@link BinNode}. This API * allows to insert and delete bytes in the binary string by referencing their * local index. * * @category Local API */ -export class BinaryApi extends NodeApi { +export class BinApi extends NodeApi { /** * Inserts octets at a given position. * @@ -462,23 +455,23 @@ export class BinaryApi extends NodeApi { } } -type ArrayRgaInnerType = N extends ArrayRga ? T : never; +type UnArrNode = N extends ArrNode ? T : never; /** - * Local changes API for the `arr` JSON CRDT node {@link ArrayRga}. This API + * Local changes API for the `arr` JSON CRDT node {@link ArrNode}. This API * allows to insert and delete elements in the array by referencing their local * index. * * @category Local API */ -export class ArrayApi = ArrayRga> extends NodeApi { +export class ArrApi = ArrNode> extends NodeApi { /** * Get API instance of a child node. * * @param index Index of the element to get. * @returns Child node API for the element at the given index. */ - public get(index: number): JsonNodeApi> { + public get(index: number): JsonNodeApi> { return this.in(index) as any; } diff --git a/src/json-crdt/model/api/types.ts b/src/json-crdt/model/api/types.ts index da62287bc7..37887dd333 100644 --- a/src/json-crdt/model/api/types.ts +++ b/src/json-crdt/model/api/types.ts @@ -1,28 +1,21 @@ -import type {Const} from '../../types/const/Const'; -import type {ArrayLww} from '../../types/lww-array/ArrayLww'; -import type {ObjectLww} from '../../types/lww-object/ObjectLww'; -import type {RootLww} from '../../types/lww-root/RootLww'; -import type {ValueLww} from '../../types/lww-value/ValueLww'; -import type {ArrayRga} from '../../types/rga-array/ArrayRga'; -import type {BinaryRga} from '../../types/rga-binary/BinaryRga'; -import type {StringRga} from '../../types/rga-string/StringRga'; -import type {ArrayApi, BinaryApi, ConstApi, ObjectApi, StringApi, VectorApi, ValueApi} from './nodes'; +import type * as types from '../../nodes'; +import type * as nodes from './nodes'; // prettier-ignore -export type JsonNodeApi = N extends Const - ? ConstApi - : N extends RootLww - ? ValueApi - : N extends ValueLww - ? ValueApi - : N extends StringRga - ? StringApi - : N extends BinaryRga - ? BinaryApi - : N extends ArrayRga - ? ArrayApi - : N extends ObjectLww - ? ObjectApi - : N extends ArrayLww - ? VectorApi +export type JsonNodeApi = N extends types.ConNode + ? nodes.ConApi + : N extends types.RootNode + ? nodes.ValApi + : N extends types.ValNode + ? nodes.ValApi + : N extends types.StrNode + ? nodes.StrApi + : N extends types.BinNode + ? nodes.BinApi + : N extends types.ArrNode + ? nodes.ArrApi + : N extends types.ObjNode + ? nodes.ObjApi + : N extends types.VecNode + ? nodes.VecApi : never; diff --git a/src/json-crdt/model/proxy/types.ts b/src/json-crdt/model/proxy/types.ts index 7a94503d51..fc0ee154ed 100644 --- a/src/json-crdt/model/proxy/types.ts +++ b/src/json-crdt/model/proxy/types.ts @@ -1,46 +1,40 @@ import type {JsonNodeApi} from '../api/types'; -import type {JsonNode, JsonNodeView} from '../../types'; -import type {Const} from '../../types/const/Const'; -import type {ArrayLww} from '../../types/lww-array/ArrayLww'; -import type {ObjectLww} from '../../types/lww-object/ObjectLww'; -import type {RootLww} from '../../types/lww-root/RootLww'; -import type {ValueLww} from '../../types/lww-value/ValueLww'; -import type {ArrayRga} from '../../types/rga-array/ArrayRga'; -import type {BinaryRga} from '../../types/rga-binary/BinaryRga'; -import type {StringRga} from '../../types/rga-string/StringRga'; +import type * as nodes from '../../nodes'; -export interface ProxyNode { +export interface ProxyNode { toApi(): JsonNodeApi; } -export type ProxyNodeConst> = ProxyNode; -export type ProxyNodeVal> = ProxyNode & {val: JsonNodeToProxyNode>}; -export type ProxyNodeVec> = ProxyNode & { - [K in keyof JsonNodeView]: JsonNodeToProxyNode[K]>; +export type ProxyNodeCon> = ProxyNode; +export type ProxyNodeVal> = ProxyNode & { + val: JsonNodeToProxyNode>; }; -export type ProxyNodeObj> = ProxyNode & { - [K in keyof JsonNodeView]: JsonNodeToProxyNode<(N extends ObjectLww ? M : never)[K]>; +export type ProxyNodeVec> = ProxyNode & { + [K in keyof nodes.JsonNodeView]: JsonNodeToProxyNode[K]>; }; -export type ProxyNodeStr = ProxyNode; -export type ProxyNodeBin = ProxyNode; -export type ProxyNodeArr> = ProxyNode & - Record ? E : never>>; +export type ProxyNodeObj> = ProxyNode & { + [K in keyof nodes.JsonNodeView]: JsonNodeToProxyNode<(N extends nodes.ObjNode ? M : never)[K]>; +}; +export type ProxyNodeStr = ProxyNode; +export type ProxyNodeBin = ProxyNode; +export type ProxyNodeArr> = ProxyNode & + Record ? E : never>>; // prettier-ignore -export type JsonNodeToProxyNode = N extends Const - ? ProxyNodeConst - : N extends RootLww +export type JsonNodeToProxyNode = N extends nodes.ConNode + ? ProxyNodeCon + : N extends nodes.RootNode ? ProxyNodeVal - : N extends ValueLww + : N extends nodes.ValNode ? ProxyNodeVal - : N extends StringRga + : N extends nodes.StrNode ? ProxyNodeStr - : N extends BinaryRga + : N extends nodes.BinNode ? ProxyNodeBin - : N extends ArrayRga + : N extends nodes.ArrNode ? ProxyNodeArr - : N extends ObjectLww + : N extends nodes.ObjNode ? ProxyNodeObj - : N extends ArrayLww + : N extends nodes.VecNode ? ProxyNodeVec : never; diff --git a/src/json-crdt/nodes/__tests__/types.spec.ts b/src/json-crdt/nodes/__tests__/types.spec.ts new file mode 100644 index 0000000000..5680399d16 --- /dev/null +++ b/src/json-crdt/nodes/__tests__/types.spec.ts @@ -0,0 +1,46 @@ +import type {ConNode, ValNode, VecNode, ArrNode, BinNode, ObjNode, StrNode} from '..'; +import type {JsonNodeView} from '../types'; + +test('can infer view type of CRDT nodes', () => { + type N1 = ConNode; + type N2 = ConNode; + type N3 = ConNode; + type N4 = ConNode<{foo: 'bar'}>; + type N5 = ValNode; + type N6 = ValNode; + type N7 = ValNode; + type N8 = StrNode; + type N9 = BinNode; + type N10 = ArrNode; + type N11 = ArrNode; + type N13 = VecNode<[N1, N2, N8, N11]>; + type N14 = ObjNode<{ + n1: N1; + n2: N2; + n3: N3; + n4: N4; + n5: N5; + n6: N6; + n7: N7; + n8: N8; + n9: N9; + n10: N10; + n11: N11; + n13: N13; + }>; + type View = JsonNodeView; + const a: View = { + n1: 123, + n2: true, + n3: 'foo', + n4: {foo: 'bar'}, + n5: 123, + n6: {foo: 'bar'}, + n7: {foo: 'bar'}, + n8: 'foo', + n9: Uint8Array.from([1, 2, 3]), + n10: [123, 123], + n11: [123, true, 123], + n13: [123, true, 'foo', [123, true, true]], + }; +}); diff --git a/src/json-crdt/types/rga-array/ArrayRga.ts b/src/json-crdt/nodes/arr/ArrNode.ts similarity index 84% rename from src/json-crdt/types/rga-array/ArrayRga.ts rename to src/json-crdt/nodes/arr/ArrNode.ts index 6ec088f3b1..90fa07fc83 100644 --- a/src/json-crdt/types/rga-array/ArrayRga.ts +++ b/src/json-crdt/nodes/arr/ArrNode.ts @@ -3,7 +3,7 @@ import {ITimestampStruct, tick} from '../../../json-crdt-patch/clock'; import {Model} from '../../model'; import {printBinary} from '../../../util/print/printBinary'; import {printTree} from '../../../util/print/printTree'; -import type {JsonNode, JsonNodeView} from '../../types'; +import type {JsonNode, JsonNodeView} from '..'; import type {Printable} from '../../../util/print/types'; type E = ITimestampStruct; @@ -14,19 +14,19 @@ const Empty = [] as any[]; * @ignore * @category CRDT Node */ -export class ArrayChunk implements Chunk { +export class ArrChunk implements Chunk { public readonly id: ITimestampStruct; public span: number; public del: boolean; public data: E[] | undefined; public len: number; - public p: ArrayChunk | undefined; - public l: ArrayChunk | undefined; - public r: ArrayChunk | undefined; - public p2: ArrayChunk | undefined; - public l2: ArrayChunk | undefined; - public r2: ArrayChunk | undefined; - public s: ArrayChunk | undefined; + public p: ArrChunk | undefined; + public l: ArrChunk | undefined; + public r: ArrChunk | undefined; + public p2: ArrChunk | undefined; + public l2: ArrChunk | undefined; + public r2: ArrChunk | undefined; + public s: ArrChunk | undefined; constructor(id: ITimestampStruct, span: number, data: E[] | undefined) { this.id = id; @@ -45,16 +45,16 @@ export class ArrayChunk implements Chunk { this.span = this.data!.length; } - public split(ticks: number): ArrayChunk { + public split(ticks: number): ArrChunk { const span = this.span; this.span = ticks; if (!this.del) { const data = this.data!; const rightData = data.splice(ticks); - const chunk = new ArrayChunk(tick(this.id, ticks), span - ticks, rightData); + const chunk = new ArrChunk(tick(this.id, ticks), span - ticks, rightData); return chunk; } - return new ArrayChunk(tick(this.id, ticks), span - ticks, undefined); + return new ArrChunk(tick(this.id, ticks), span - ticks, undefined); } public delete(): void { @@ -62,8 +62,8 @@ export class ArrayChunk implements Chunk { this.data = undefined; } - public clone(): ArrayChunk { - return new ArrayChunk(this.id, this.span, this.data ? [...this.data] : undefined); + public clone(): ArrChunk { + return new ArrChunk(this.id, this.span, this.data ? [...this.data] : undefined); } } @@ -73,7 +73,7 @@ export class ArrayChunk implements Chunk { * * @category CRDT Node */ -export class ArrayRga +export class ArrNode extends AbstractRga implements JsonNode[]>>, Printable { @@ -115,8 +115,8 @@ export class ArrayRga // -------------------------------------------------------------- AbstractRga /** @ignore */ - public createChunk(id: ITimestampStruct, data: E[] | undefined): ArrayChunk { - return new ArrayChunk(id, data ? data.length : 0, data); + public createChunk(id: ITimestampStruct, data: E[] | undefined): ArrChunk { + return new ArrChunk(id, data ? data.length : 0, data); } /** @ignore */ @@ -125,7 +125,7 @@ export class ArrayRga } protected toStringName(): string { - return super.toStringName() + ' "arr"'; + return super.toStringName(); } // ----------------------------------------------------------------- JsonNode @@ -177,7 +177,7 @@ export class ArrayRga // ---------------------------------------------------------------- Printable /** @ignore */ - protected printChunk(tab: string, chunk: ArrayChunk): string { + protected printChunk(tab: string, chunk: ArrChunk): string { const pos = this.pos(chunk); let valueTree = ''; if (!chunk.del) { diff --git a/src/json-crdt/types/rga-array/__tests__/ArrayRga.spec.ts b/src/json-crdt/nodes/arr/__tests__/ArrNode.spec.ts similarity index 90% rename from src/json-crdt/types/rga-array/__tests__/ArrayRga.spec.ts rename to src/json-crdt/nodes/arr/__tests__/ArrNode.spec.ts index e6ee97f06b..6faf4f5231 100644 --- a/src/json-crdt/types/rga-array/__tests__/ArrayRga.spec.ts +++ b/src/json-crdt/nodes/arr/__tests__/ArrNode.spec.ts @@ -1,6 +1,6 @@ import {PatchBuilder} from '../../../../json-crdt-patch/PatchBuilder'; import {Model} from '../../../model'; -import {ArrayRga} from '../ArrayRga'; +import {ArrNode} from '../ArrNode'; test('can insert two booleans into an array', () => { const doc = Model.withLogicalClock(); @@ -20,7 +20,7 @@ test('can insert two booleans into an array', () => { const ins2 = builder3.insArr(arr, ins1, [f]); doc.applyPatch(builder3.patch); - const node = doc.index.get(arr) as ArrayRga; + const node = doc.index.get(arr) as ArrNode; const firstChunk = node.first(); expect(node.length()).toBe(2); diff --git a/src/json-crdt/types/rga-binary/BinaryRga.ts b/src/json-crdt/nodes/bin/BinNode.ts similarity index 73% rename from src/json-crdt/types/rga-binary/BinaryRga.ts rename to src/json-crdt/nodes/bin/BinNode.ts index b96609ccc1..244200cee8 100644 --- a/src/json-crdt/types/rga-binary/BinaryRga.ts +++ b/src/json-crdt/nodes/bin/BinNode.ts @@ -1,4 +1,4 @@ -import type {JsonNode} from '../../types'; +import type {JsonNode} from '..'; import {ITimestampStruct, tick} from '../../../json-crdt-patch/clock'; import {AbstractRga, Chunk} from '../rga/AbstractRga'; @@ -6,19 +6,19 @@ import {AbstractRga, Chunk} from '../rga/AbstractRga'; * @ignore * @category CRDT Node */ -export class BinaryChunk implements Chunk { +export class BinChunk implements Chunk { public readonly id: ITimestampStruct; public span: number; public del: boolean; public data: Uint8Array | undefined; public len: number; - public p: BinaryChunk | undefined; - public l: BinaryChunk | undefined; - public r: BinaryChunk | undefined; - public p2: BinaryChunk | undefined; - public l2: BinaryChunk | undefined; - public r2: BinaryChunk | undefined; - public s: BinaryChunk | undefined; + public p: BinChunk | undefined; + public l: BinChunk | undefined; + public r: BinChunk | undefined; + public p2: BinChunk | undefined; + public l2: BinChunk | undefined; + public r2: BinChunk | undefined; + public s: BinChunk | undefined; constructor(id: ITimestampStruct, span: number, buf: Uint8Array | undefined) { this.id = id; @@ -41,16 +41,16 @@ export class BinaryChunk implements Chunk { this.span = combined.length; } - public split(ticks: number): BinaryChunk { + public split(ticks: number): BinChunk { if (!this.del) { const data = this.data!; const rightBuffer = data.subarray(ticks); - const chunk = new BinaryChunk(tick(this.id, ticks), this.span - ticks, rightBuffer); + const chunk = new BinChunk(tick(this.id, ticks), this.span - ticks, rightBuffer); this.data = data.subarray(0, ticks); this.span = ticks; return chunk; } - const chunk = new BinaryChunk(tick(this.id, ticks), this.span - ticks, undefined); + const chunk = new BinChunk(tick(this.id, ticks), this.span - ticks, undefined); this.span = ticks; return chunk; } @@ -60,8 +60,8 @@ export class BinaryChunk implements Chunk { this.data = undefined; } - public clone(): BinaryChunk { - const chunk = new BinaryChunk(this.id, this.span, this.data); + public clone(): BinChunk { + const chunk = new BinChunk(this.id, this.span, this.data); return chunk; } } @@ -72,7 +72,7 @@ export class BinaryChunk implements Chunk { * * @category CRDT Node */ -export class BinaryRga extends AbstractRga implements JsonNode { +export class BinNode extends AbstractRga implements JsonNode { // ----------------------------------------------------------------- JsonNode /** @ignore */ @@ -112,8 +112,8 @@ export class BinaryRga extends AbstractRga implements JsonNode implements JsonNode new Uint8Array(octets); describe('.insAt()', () => { test('can insert into empty blob', () => { - const type = new BinaryRga(ts(1, 1)); + const type = new BinNode(ts(1, 1)); type.insAt(0, ts(1, 2), b(1, 2, 3)); expect(type.view()).toEqual(b(1, 2, 3)); }); test('can insert at the beginning of a blob', () => { - const type = new BinaryRga(ts(1, 1)); + const type = new BinNode(ts(1, 1)); type.insAt(0, ts(1, 2), b(1, 2, 3)); type.insAt(0, ts(1, 66), b(0)); expect(type.view()).toEqual(b(0, 1, 2, 3)); @@ -20,7 +20,7 @@ describe('.insAt()', () => { }); test('can insert at the end of blob with sequential ID', () => { - const type = new BinaryRga(ts(1, 1)); + const type = new BinNode(ts(1, 1)); type.insAt(0, ts(1, 2), b(1)); expect(type.view()).toEqual(b(1)); type.insAt(1, ts(1, 3), b(2)); @@ -28,7 +28,7 @@ describe('.insAt()', () => { }); test('can insert at the end of blob with ID jump', () => { - const type = new BinaryRga(ts(1, 1)); + const type = new BinNode(ts(1, 1)); type.insAt(0, ts(1, 2), b(1)); expect(type.view()).toEqual(b(1)); type.insAt(1, ts(1, 33), b(2)); diff --git a/src/json-crdt/types/rga-binary/__tests__/BinaryRga.fuzzing.spec.ts b/src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts similarity index 84% rename from src/json-crdt/types/rga-binary/__tests__/BinaryRga.fuzzing.spec.ts rename to src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts index 38c238d869..f030804436 100644 --- a/src/json-crdt/types/rga-binary/__tests__/BinaryRga.fuzzing.spec.ts +++ b/src/json-crdt/nodes/bin/__tests__/BinNode.fuzzing.spec.ts @@ -2,16 +2,16 @@ import {ITimespanStruct, ITimestampStruct, ts} from '../../../../json-crdt-patch/clock'; import {Fuzzer} from '../../../../util/Fuzzer'; -import {BinaryRga} from '../BinaryRga'; +import {BinNode} from '../BinNode'; import {randomU32} from 'hyperdyperid/lib/randomU32'; import {RandomJson} from '../../../../json-random'; import * as path from 'path'; import * as fs from 'fs'; type IdGenerator = (span: number) => ITimestampStruct; -type Operation = ((rga: BinaryRga) => void) & {toString: (symb?: string) => string}; +type Operation = ((rga: BinNode) => void) & {toString: (symb?: string) => string}; -class BinaryRgaFuzzer extends Fuzzer { +class BinNodeFuzzer extends Fuzzer { t1: number = 1; t2: number = 1; @@ -27,10 +27,10 @@ class BinaryRgaFuzzer extends Fuzzer { return id; }; - rga1 = new BinaryRga(ts(1, 0)); - rga2 = new BinaryRga(ts(1, 0)); + rga1 = new BinNode(ts(1, 0)); + rga2 = new BinNode(ts(1, 0)); - operation = (source: BinaryRga, id: IdGenerator): Operation => { + operation = (source: BinNode, id: IdGenerator): Operation => { const doInsert = !source.length() || !randomU32(0, 1); if (doInsert) { const pos = randomU32(0, source.length() - 1); @@ -38,7 +38,7 @@ class BinaryRgaFuzzer extends Fuzzer { const after = source.find(pos) || source.id; const buf = RandomJson.genBinary(length); const stamp = id(length); - const op = (rga: BinaryRga) => { + const op = (rga: BinNode) => { rga.ins(after, stamp, buf); }; op.toString = (symb: string = 'rga') => `${symb}.ins(${tsToStr(after)}, ${tsToStr(stamp)}, ${uint8ToStr(buf)});`; @@ -47,7 +47,7 @@ class BinaryRgaFuzzer extends Fuzzer { const pos = randomU32(0, source.length() - 1); const length = randomU32(0, source.length() - pos); const range = source.findInterval(pos, length); - const op = (rga: BinaryRga) => { + const op = (rga: BinNode) => { rga.delete(range); }; op.toString = (symb: string = 'rga') => `${symb}.delete([${range.map(tssToStr).join(', ')}]);`; @@ -65,7 +65,7 @@ const tsToStr = (stamp: ITimestampStruct) => `ts(${stamp.sid}, ${stamp.time})`; const tssToStr = (span: ITimespanStruct) => `tss(${span.sid}, ${span.time}, ${span.span})`; const uint8ToStr = (buf: Uint8Array) => `new Uint8Array([${buf}])`; -const assertEmptyNodes = (rga: BinaryRga) => { +const assertEmptyNodes = (rga: BinNode) => { for (let chunk = rga.first(); chunk; chunk = rga.next(chunk)) { if (chunk.del) { if (chunk.data) { @@ -79,16 +79,16 @@ const assertEmptyNodes = (rga: BinaryRga) => { } }; -test('fuzzing BinaryRga', () => { +test('fuzzing BinNode', () => { for (let j = 0; j < 1000; j++) { - const fuzzer = new BinaryRgaFuzzer(); + const fuzzer = new BinNodeFuzzer(); const lines: string[] = []; lines.push(`import {ts, tss} from "../../../../json-crdt-patch/clock";`); - lines.push(`import {BinaryRga} from "../BinaryRga";`); + lines.push(`import {BinNode} from "../BinNode";`); lines.push(''); lines.push(`test('two concurrent users - #', () => {`); - lines.push(` const rga1 = new BinaryRga(${tsToStr(fuzzer.rga1.id)});`); - lines.push(` const rga2 = new BinaryRga(${tsToStr(fuzzer.rga2.id)});`); + lines.push(` const rga1 = new BinNode(${tsToStr(fuzzer.rga1.id)});`); + lines.push(` const rga2 = new BinNode(${tsToStr(fuzzer.rga2.id)});`); lines.push(''); let oldOp1: Operation | undefined; diff --git a/src/json-crdt/types/rga-binary/__tests__/BinaryRga.spec.ts b/src/json-crdt/nodes/bin/__tests__/BinNode.spec.ts similarity index 90% rename from src/json-crdt/types/rga-binary/__tests__/BinaryRga.spec.ts rename to src/json-crdt/nodes/bin/__tests__/BinNode.spec.ts index 48e758aa2b..acfd1f124d 100644 --- a/src/json-crdt/types/rga-binary/__tests__/BinaryRga.spec.ts +++ b/src/json-crdt/nodes/bin/__tests__/BinNode.spec.ts @@ -1,7 +1,7 @@ import {tick} from '../../../../json-crdt-patch/clock'; import {PatchBuilder} from '../../../../json-crdt-patch/PatchBuilder'; import {Model} from '../../../model'; -import {BinaryRga} from '../BinaryRga'; +import {BinNode} from '../BinNode'; test('merges sequential chunks', () => { const doc = Model.withLogicalClock(); @@ -19,7 +19,7 @@ test('merges sequential chunks', () => { const ins2 = builder3.insBin(bin, tick(ins1, 1), new Uint8Array([3, 4])); doc.applyPatch(builder3.patch); - const node = doc.index.get(bin) as BinaryRga; + const node = doc.index.get(bin) as BinNode; const firstChunk = node.first(); expect(firstChunk!.data).toStrictEqual(new Uint8Array([1, 2, 3, 4])); diff --git a/src/json-crdt/types/const/Const.ts b/src/json-crdt/nodes/con/ConNode.ts similarity index 89% rename from src/json-crdt/types/const/Const.ts rename to src/json-crdt/nodes/con/ConNode.ts index e1e962daa1..0ba4bfc8c1 100644 --- a/src/json-crdt/types/const/Const.ts +++ b/src/json-crdt/nodes/con/ConNode.ts @@ -11,7 +11,7 @@ import type {Printable} from '../../../util/print/types'; * * @category CRDT Node */ -export class Const implements JsonNode, Printable { +export class ConNode implements JsonNode, Printable { /** * @param id ID of the CRDT node. * @param val Raw value of the constant. It can be any JSON/CBOR value, or @@ -57,6 +57,6 @@ export class Const implements JsonNode, val instanceof Uint8Array ? `Uint8Array { ${('' + val).replaceAll(',', ', ')} }` : `{ ${val instanceof Timestamp ? toDisplayString(val as Timestamp) : JSON.stringify(val)} }`; - return `${this.constructor.name} "con" ${toDisplayString(this.id)} ${valFormatted}`; + return `${this.constructor.name} ${toDisplayString(this.id)} ${valFormatted}`; } } diff --git a/src/json-crdt/types/index.ts b/src/json-crdt/nodes/index.ts similarity index 100% rename from src/json-crdt/types/index.ts rename to src/json-crdt/nodes/index.ts diff --git a/src/json-crdt/nodes/nodes.ts b/src/json-crdt/nodes/nodes.ts new file mode 100644 index 0000000000..74827c31cc --- /dev/null +++ b/src/json-crdt/nodes/nodes.ts @@ -0,0 +1,29 @@ +import type {ITimestampStruct} from '../../json-crdt-patch/clock'; +import type {ConNode} from './con/ConNode'; +import type {ValNode} from './val/ValNode'; +import type {VecNode} from './vec/VecNode'; +import type {JsonNode} from './types'; +import type {ObjNode} from './obj/ObjNode'; +import type {StrNode} from './str/StrNode'; +import type {BinNode} from './bin/BinNode'; +import type {ArrNode} from './arr/ArrNode'; + +// tslint:disable-next-line:no-namespace +export namespace n { + export type con = ConNode; + export type val = ValNode; + export type vec = VecNode; + export type obj = Record> = ObjNode; + export type str = StrNode; + export type bin = BinNode; + export type arr = ArrNode; +} + +export {ConNode} from './con/ConNode'; +export {ValNode} from './val/ValNode'; +export {RootNode} from './root/RootNode'; +export {VecNode} from './vec/VecNode'; +export {ObjNode} from './obj/ObjNode'; +export {ArrNode, ArrChunk} from './arr/ArrNode'; +export {BinNode, BinChunk} from './bin/BinNode'; +export {StrNode, StrChunk} from './str/StrNode'; diff --git a/src/json-crdt/types/lww-object/ObjectLww.ts b/src/json-crdt/nodes/obj/ObjNode.ts similarity index 96% rename from src/json-crdt/types/lww-object/ObjectLww.ts rename to src/json-crdt/nodes/obj/ObjNode.ts index 3a9d152dd3..2feda415ee 100644 --- a/src/json-crdt/types/lww-object/ObjectLww.ts +++ b/src/json-crdt/nodes/obj/ObjNode.ts @@ -2,7 +2,7 @@ import {compare, ITimestampStruct, toDisplayString} from '../../../json-crdt-pat import {printTree} from '../../../util/print/printTree'; import type {Model} from '../../model'; import type {Printable} from '../../../util/print/types'; -import type {JsonNode, JsonNodeView} from '../../types'; +import type {JsonNode, JsonNodeView} from '..'; /** * Represents a `obj` JSON CRDT node, which is a Last-write-wins (LWW) object. @@ -11,7 +11,8 @@ import type {JsonNode, JsonNodeView} from '../../types'; * * @category CRDT Node */ -export class ObjectLww = Record> + +export class ObjNode = Record> implements JsonNode>>, Printable { /** diff --git a/src/json-crdt/types/rga/AbstractRga.ts b/src/json-crdt/nodes/rga/AbstractRga.ts similarity index 100% rename from src/json-crdt/types/rga/AbstractRga.ts rename to src/json-crdt/nodes/rga/AbstractRga.ts diff --git a/src/json-crdt/types/rga/README.md b/src/json-crdt/nodes/rga/README.md similarity index 100% rename from src/json-crdt/types/rga/README.md rename to src/json-crdt/nodes/rga/README.md diff --git a/src/json-crdt/types/rga/index.ts b/src/json-crdt/nodes/rga/index.ts similarity index 100% rename from src/json-crdt/types/rga/index.ts rename to src/json-crdt/nodes/rga/index.ts diff --git a/src/json-crdt/types/lww-root/RootLww.ts b/src/json-crdt/nodes/root/RootNode.ts similarity index 74% rename from src/json-crdt/types/lww-root/RootLww.ts rename to src/json-crdt/nodes/root/RootNode.ts index eb1d6e4da4..89882d1340 100644 --- a/src/json-crdt/types/lww-root/RootLww.ts +++ b/src/json-crdt/nodes/root/RootNode.ts @@ -1,17 +1,17 @@ import {ORIGIN, SESSION} from '../../../json-crdt-patch/constants'; -import {ValueLww} from '../lww-value/ValueLww'; +import {ValNode} from '../val/ValNode'; import {Model, UNDEFINED} from '../../model/Model'; import type {ITimestampStruct} from '../../../json-crdt-patch/clock'; import type {JsonNode} from '../types'; /** - * The root of a JSON CRDT document. {@link RootLww} is a {@link ValueLww} with + * The root of a JSON CRDT document. {@link RootNode} is a {@link ValNode} with * a special `0.0` ID, which is always the same. It is used to represent the * root of a document. * * @category CRDT Node */ -export class RootLww extends ValueLww { +export class RootNode extends ValNode { /** * @param val Latest value of the document root. */ diff --git a/src/json-crdt/types/rga-string/StringRga.ts b/src/json-crdt/nodes/str/StrNode.ts similarity index 70% rename from src/json-crdt/types/rga-string/StringRga.ts rename to src/json-crdt/nodes/str/StrNode.ts index 1e75df8a8d..2064d926b2 100644 --- a/src/json-crdt/types/rga-string/StringRga.ts +++ b/src/json-crdt/nodes/str/StrNode.ts @@ -1,25 +1,25 @@ import {ITimestampStruct, tick} from '../../../json-crdt-patch/clock'; import {AbstractRga, Chunk} from '../rga/AbstractRga'; import {next} from '../../../util/trees/util'; -import type {JsonNode} from '../../types'; +import type {JsonNode} from '..'; /** * @ignore * @category CRDT Node */ -export class StringChunk implements Chunk { +export class StrChunk implements Chunk { public readonly id: ITimestampStruct; public span: number; public del: boolean; public data: string; public len: number; - public p: StringChunk | undefined; - public l: StringChunk | undefined; - public r: StringChunk | undefined; - public p2: StringChunk | undefined; - public l2: StringChunk | undefined; - public r2: StringChunk | undefined; - public s: StringChunk | undefined; + public p: StrChunk | undefined; + public l: StrChunk | undefined; + public r: StrChunk | undefined; + public p2: StrChunk | undefined; + public l2: StrChunk | undefined; + public r2: StrChunk | undefined; + public s: StrChunk | undefined; constructor(id: ITimestampStruct, span: number, str: string) { this.id = id; @@ -41,14 +41,14 @@ export class StringChunk implements Chunk { this.span = this.data.length; } - public split(ticks: number): StringChunk { + public split(ticks: number): StrChunk { if (!this.del) { - const chunk = new StringChunk(tick(this.id, ticks), this.span - ticks, this.data.slice(ticks)); + const chunk = new StrChunk(tick(this.id, ticks), this.span - ticks, this.data.slice(ticks)); this.data = this.data.slice(0, ticks); this.span = ticks; return chunk; } - const chunk = new StringChunk(tick(this.id, ticks), this.span - ticks, ''); + const chunk = new StrChunk(tick(this.id, ticks), this.span - ticks, ''); this.span = ticks; return chunk; } @@ -58,8 +58,8 @@ export class StringChunk implements Chunk { this.data = ''; } - public clone(): StringChunk { - const chunk = new StringChunk(this.id, this.span, this.data); + public clone(): StrChunk { + const chunk = new StrChunk(this.id, this.span, this.data); return chunk; } } @@ -70,7 +70,7 @@ export class StringChunk implements Chunk { * * @category CRDT Node */ -export class StringRga extends AbstractRga implements JsonNode { +export class StrNode extends AbstractRga implements JsonNode { // ----------------------------------------------------------------- JsonNode /** @ignore */ @@ -103,8 +103,8 @@ export class StringRga extends AbstractRga im // -------------------------------------------------------------- AbstractRga /** @ignore */ - public createChunk(id: ITimestampStruct, str: string | undefined): StringChunk { - return new StringChunk(id, str ? str.length : 0, str || ''); + public createChunk(id: ITimestampStruct, str: string | undefined): StrChunk { + return new StrChunk(id, str ? str.length : 0, str || ''); } /** @ignore */ @@ -113,6 +113,6 @@ export class StringRga extends AbstractRga im } protected toStringName(): string { - return super.toStringName() + ' "str"'; + return super.toStringName(); } } diff --git a/src/json-crdt/types/rga-string/__tests__/StringRga-pos.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode-pos.spec.ts similarity index 93% rename from src/json-crdt/types/rga-string/__tests__/StringRga-pos.spec.ts rename to src/json-crdt/nodes/str/__tests__/StrNode-pos.spec.ts index ca7376b3d0..c68bc883b0 100644 --- a/src/json-crdt/types/rga-string/__tests__/StringRga-pos.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode-pos.spec.ts @@ -1,12 +1,12 @@ /* tslint:disable no-console */ -import {StringRga} from '../StringRga'; +import {StrNode} from '../StrNode'; import {LogicalClock} from '../../../../json-crdt-patch/clock'; describe('.pos()', () => { test('correctly returns positions of a characters in alphabet', () => { const clock = new LogicalClock(123, 1); - const str = new StringRga(clock.tick(1)); + const str = new StrNode(clock.tick(1)); const txt = 'abcdefghijklmnopqrstuvwxyz'; str.insAt(0, clock.tick(txt.length), txt); for (let i = 0; i < txt.length; i++) { @@ -18,7 +18,7 @@ describe('.pos()', () => { test('correctly returns positions of a characters in alphabet (characters inserted in reverse)', () => { const clock = new LogicalClock(123, 1); - const str = new StringRga(clock.tick(1)); + const str = new StrNode(clock.tick(1)); str.insAt(0, clock.tick(1), 'z'); str.insAt(0, clock.tick(1), 'y'); str.insAt(0, clock.tick(1), 'x'); diff --git a/src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing-2.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts similarity index 83% rename from src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing-2.spec.ts rename to src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts index 7ec891bbc0..ff7b7222d8 100644 --- a/src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing-2.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-2.spec.ts @@ -2,7 +2,7 @@ import {ITimespanStruct, ITimestampStruct, ts} from '../../../../json-crdt-patch/clock'; import {Fuzzer} from '../../../../util/Fuzzer'; -import {StringRga} from '../StringRga'; +import {StrNode} from '../StrNode'; import {randomU32} from 'hyperdyperid/lib/randomU32'; import * as path from 'path'; import * as fs from 'fs'; @@ -11,9 +11,9 @@ const generateString = (length: number) => Fuzzer.pick([() => 'a'.repeat(length), () => 'b'.repeat(length), () => 'c'.repeat(length), () => 'd'.repeat(length)]); type IdGenerator = (span: number) => ITimestampStruct; -type Operation = ((rga: StringRga) => void) & {toString: (symb?: string) => string}; +type Operation = ((rga: StrNode) => void) & {toString: (symb?: string) => string}; -class BinaryRgaFuzzer extends Fuzzer { +class BinNodeFuzzer extends Fuzzer { t1: number = 1; t2: number = 1; @@ -29,10 +29,10 @@ class BinaryRgaFuzzer extends Fuzzer { return id; }; - rga1 = new StringRga(ts(1, 0)); - rga2 = new StringRga(ts(1, 0)); + rga1 = new StrNode(ts(1, 0)); + rga2 = new StrNode(ts(1, 0)); - operation = (source: StringRga, id: IdGenerator): Operation => { + operation = (source: StrNode, id: IdGenerator): Operation => { const doInsert = !source.length() || !randomU32(0, 1); if (doInsert) { const pos = randomU32(0, source.length() - 1); @@ -40,7 +40,7 @@ class BinaryRgaFuzzer extends Fuzzer { const after = source.find(pos) || source.id; const content = generateString(length)(); const stamp = id(length); - const op = (rga: StringRga) => { + const op = (rga: StrNode) => { rga.ins(after, stamp, content); }; op.toString = (symb: string = 'rga') => @@ -50,7 +50,7 @@ class BinaryRgaFuzzer extends Fuzzer { const pos = randomU32(0, source.length() - 1); const length = randomU32(0, source.length() - pos); const range = source.findInterval(pos, length); - const op = (rga: StringRga) => { + const op = (rga: StrNode) => { rga.delete(range); }; op.toString = (symb: string = 'rga') => `${symb}.delete([${range.map(tssToStr).join(', ')}]);`; @@ -67,16 +67,16 @@ class BinaryRgaFuzzer extends Fuzzer { const tsToStr = (stamp: ITimestampStruct) => `ts(${stamp.sid}, ${stamp.time})`; const tssToStr = (span: ITimespanStruct) => `tss(${span.sid}, ${span.time}, ${span.span})`; -test('fuzzing StringRga', () => { +test('fuzzing StrNode', () => { for (let j = 0; j < 1000; j++) { - const fuzzer = new BinaryRgaFuzzer(); + const fuzzer = new BinNodeFuzzer(); const lines: string[] = []; lines.push(`import {ts, tss} from "../../../../json-crdt-patch/clock";`); - lines.push(`import {StringRga} from "../StringRga";`); + lines.push(`import {StrNode} from "../StrNode";`); lines.push(''); lines.push(`test('two concurrent users - #', () => {`); - lines.push(` const rga1 = new StringRga(${tsToStr(fuzzer.rga1.id)});`); - lines.push(` const rga2 = new StringRga(${tsToStr(fuzzer.rga2.id)});`); + lines.push(` const rga1 = new StrNode(${tsToStr(fuzzer.rga1.id)});`); + lines.push(` const rga2 = new StrNode(${tsToStr(fuzzer.rga2.id)});`); lines.push(''); for (let i = 0; i < 10; i++) { diff --git a/src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing-multiuser.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-multiuser.spec.ts similarity index 87% rename from src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing-multiuser.spec.ts rename to src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-multiuser.spec.ts index 61e9a15174..fd005217c7 100644 --- a/src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing-multiuser.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing-multiuser.spec.ts @@ -1,8 +1,8 @@ -import {StringRgaFuzzer, StringRgaFuzzerOptions} from './StringRgaFuzzer'; +import {StrNodeFuzzer, StrNodeFuzzerOptions} from './StrNodeFuzzer'; -const execute = (times: number, options?: Partial) => { +const execute = (times: number, options?: Partial) => { for (let i = 0; i < times; i++) { - const fuzzer = new StringRgaFuzzer(options); + const fuzzer = new StrNodeFuzzer(options); fuzzer.generatePrelude(); try { fuzzer.assertSiteViewsEqual(); diff --git a/src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts similarity index 86% rename from src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing.spec.ts rename to src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts index 9e810f7412..9ab6cbe899 100644 --- a/src/json-crdt/types/rga-string/__tests__/StringRga.fuzzing.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode.fuzzing.spec.ts @@ -2,16 +2,16 @@ import {ITimespanStruct, ITimestampStruct, ts} from '../../../../json-crdt-patch/clock'; import {Fuzzer} from '../../../../util/Fuzzer'; -import {StringRga} from '../StringRga'; +import {StrNode} from '../StrNode'; import {randomU32} from 'hyperdyperid/lib/randomU32'; import {RandomJson} from '../../../../json-random'; import * as path from 'path'; import * as fs from 'fs'; type IdGenerator = (span: number) => ITimestampStruct; -type Operation = ((rga: StringRga) => void) & {toString: (symb?: string) => string}; +type Operation = ((rga: StrNode) => void) & {toString: (symb?: string) => string}; -class BinaryRgaFuzzer extends Fuzzer { +class BinNodeFuzzer extends Fuzzer { t1: number = 1; t2: number = 1; @@ -27,10 +27,10 @@ class BinaryRgaFuzzer extends Fuzzer { return id; }; - rga1 = new StringRga(ts(1, 0)); - rga2 = new StringRga(ts(1, 0)); + rga1 = new StrNode(ts(1, 0)); + rga2 = new StrNode(ts(1, 0)); - operation = (source: StringRga, id: IdGenerator): Operation => { + operation = (source: StrNode, id: IdGenerator): Operation => { const doInsert = !source.length() || !randomU32(0, 1); if (doInsert) { const pos = randomU32(0, source.length() - 1); @@ -38,7 +38,7 @@ class BinaryRgaFuzzer extends Fuzzer { const after = source.find(pos) || source.id; const content = RandomJson.genString(length); const stamp = id(length); - const op = (rga: StringRga) => { + const op = (rga: StrNode) => { rga.ins(after, stamp, content); }; op.toString = (symb: string = 'rga') => @@ -48,7 +48,7 @@ class BinaryRgaFuzzer extends Fuzzer { const pos = randomU32(0, source.length() - 1); const length = randomU32(0, source.length() - pos); const range = source.findInterval(pos, length); - const op = (rga: StringRga) => { + const op = (rga: StrNode) => { rga.delete(range); }; op.toString = (symb: string = 'rga') => `${symb}.delete([${range.map(tssToStr).join(', ')}]);`; @@ -65,16 +65,16 @@ class BinaryRgaFuzzer extends Fuzzer { const tsToStr = (stamp: ITimestampStruct) => `ts(${stamp.sid}, ${stamp.time})`; const tssToStr = (span: ITimespanStruct) => `tss(${span.sid}, ${span.time}, ${span.span})`; -test('fuzzing StringRga', () => { +test('fuzzing StrNode', () => { for (let j = 0; j < 1000; j++) { - const fuzzer = new BinaryRgaFuzzer(); + const fuzzer = new BinNodeFuzzer(); const lines: string[] = []; lines.push(`import {ts, tss} from "../../../../json-crdt-patch/clock";`); - lines.push(`import {StringRga} from "../StringRga";`); + lines.push(`import {StrNode} from "../StrNode";`); lines.push(''); lines.push(`test('two concurrent users - #', () => {`); - lines.push(` const rga1 = new StringRga(${tsToStr(fuzzer.rga1.id)});`); - lines.push(` const rga2 = new StringRga(${tsToStr(fuzzer.rga2.id)});`); + lines.push(` const rga1 = new StrNode(${tsToStr(fuzzer.rga1.id)});`); + lines.push(` const rga2 = new StrNode(${tsToStr(fuzzer.rga2.id)});`); lines.push(''); let oldOp1: Operation | undefined; diff --git a/src/json-crdt/types/rga-string/__tests__/StringRga.spec.ts b/src/json-crdt/nodes/str/__tests__/StrNode.spec.ts similarity index 91% rename from src/json-crdt/types/rga-string/__tests__/StringRga.spec.ts rename to src/json-crdt/nodes/str/__tests__/StrNode.spec.ts index 1601e61fe5..37e887d49d 100644 --- a/src/json-crdt/types/rga-string/__tests__/StringRga.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNode.spec.ts @@ -1,11 +1,11 @@ /* tslint:disable no-console */ -import {StringRga, StringChunk} from '../StringRga'; +import {StrNode, StrChunk} from '../StrNode'; import {equal, ITimespanStruct, ITimestampStruct, tick, ts, tss} from '../../../../json-crdt-patch/clock'; import {prev} from '../../../../util/trees/util'; /** Validates that .find() method returns correct timestamp for every position. */ -const assertFind = (type: StringRga) => { +const assertFind = (type: StrNode) => { const view = type.view(); const ids: ITimestampStruct[] = []; let curr = type.first(); @@ -31,12 +31,12 @@ const assertFind = (type: StringRga) => { expect(type.find(view.length + 1)).toBe(undefined); }; -const chunks = function* (rga: StringRga): IterableIterator { - for (let chunk = rga.first(); chunk; chunk = rga.next(chunk)) yield chunk as StringChunk; +const chunks = function* (rga: StrNode): IterableIterator { + for (let chunk = rga.first(); chunk; chunk = rga.next(chunk)) yield chunk as StrChunk; }; /** Verifies that all chunk parents are connected and chunk IDs are in B+Tree. */ -const assetTreeIsValid = (tree: StringRga, chunk: StringChunk | undefined = tree.root as StringChunk | undefined) => { +const assetTreeIsValid = (tree: StrNode, chunk: StrChunk | undefined = tree.root as StrChunk | undefined) => { if (!chunk) return; if (chunk.l && chunk.l.p !== chunk) { console.log('Chunk', chunk, 'has invalid left parent', chunk.l); @@ -53,7 +53,7 @@ const assetTreeIsValid = (tree: StringRga, chunk: StringChunk | undefined = tree if (tree.root === chunk) { let str = ''; let size = 0; - let prev: StringChunk | undefined = undefined; + let prev: StrChunk | undefined = undefined; for (const chunk of chunks(tree)) { size++; str += chunk.data ? chunk.data : ''; @@ -83,7 +83,7 @@ const assetTreeIsValid = (tree: StringRga, chunk: StringChunk | undefined = tree } } } - prev = chunk as StringChunk; + prev = chunk as StrChunk; } // Check RGA size is correct. if (tree.size() !== size) { @@ -109,11 +109,11 @@ const assetTreeIsValid = (tree: StringRga, chunk: StringChunk | undefined = tree describe('binary tree', () => { const createTree = () => { - const type = new StringRga(ts(1, 0)); + const type = new StrNode(ts(1, 0)); let id = 1; - const createNode = (l?: StringChunk, r?: StringChunk) => { + const createNode = (l?: StrChunk, r?: StrChunk) => { const time = id++; - const chunk = new StringChunk(ts(1, time), ('a' + time).length, 'a' + time); + const chunk = new StrChunk(ts(1, time), ('a' + time).length, 'a' + time); return chunk; }; const n1 = createNode(); @@ -153,22 +153,22 @@ describe('binary tree', () => { test('can print tree layout', () => { const tree = createTree(); expect(tree.toString()).toMatchInlineSnapshot(` - "StringRga "str" .0 { "a1a2a3a4a5a6a7a8a9a10a11a12a13a1" … } - └─ StringChunk .8!2 len:36 { "a8" } - ← StringChunk .4!2 len:14 { "a4" } - ← StringChunk .2!2 len:6 { "a2" } - ← StringChunk .1!2 len:2 { "a1" } - → StringChunk .3!2 len:2 { "a3" } - → StringChunk .6!2 len:6 { "a6" } - ← StringChunk .5!2 len:2 { "a5" } - → StringChunk .7!2 len:2 { "a7" } - → StringChunk .12!3 len:20 { "a12" } - ← StringChunk .10!3 len:8 { "a10" } - ← StringChunk .9!2 len:2 { "a9" } - → StringChunk .11!3 len:3 { "a11" } - → StringChunk .14!3 len:9 { "a14" } - ← StringChunk .13!3 len:3 { "a13" } - → StringChunk .15!3 len:3 { "a15" }" + "StrNode .0 { "a1a2a3a4a5a6a7a8a9a10a11a12a13a1" … } + └─ StrChunk .8!2 len:36 { "a8" } + ← StrChunk .4!2 len:14 { "a4" } + ← StrChunk .2!2 len:6 { "a2" } + ← StrChunk .1!2 len:2 { "a1" } + → StrChunk .3!2 len:2 { "a3" } + → StrChunk .6!2 len:6 { "a6" } + ← StrChunk .5!2 len:2 { "a5" } + → StrChunk .7!2 len:2 { "a7" } + → StrChunk .12!3 len:20 { "a12" } + ← StrChunk .10!3 len:8 { "a10" } + ← StrChunk .9!2 len:2 { "a9" } + → StrChunk .11!3 len:3 { "a11" } + → StrChunk .14!3 len:9 { "a14" } + ← StrChunk .13!3 len:3 { "a13" } + → StrChunk .15!3 len:3 { "a15" }" `); }); @@ -501,17 +501,17 @@ describe('binary tree', () => { }); }); -describe('StringRga', () => { +describe('StrNode', () => { describe('.ins()', () => { test('is empty string by default', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); expect(type.view()).toBe(''); assetTreeIsValid(type); }); test('simple insertion produces two chunks', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '1234'); type.ins(ts(1, 5), ts(1, 33), '5678'); let cnt = 1; @@ -522,7 +522,7 @@ describe('StringRga', () => { test('ignores operations where "after" ID is not found', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); const after = ts(1, 123); const id2 = ts(1, 2); type.ins(after, id2, 'a'); @@ -532,7 +532,7 @@ describe('StringRga', () => { test('can insert at root', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); const id2 = ts(1, 2); type.ins(id1, id2, 'a'); expect(type.view()).toBe('a'); @@ -541,7 +541,7 @@ describe('StringRga', () => { test('can merge subsequent ID chunks', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); const id2 = ts(1, 2); type.ins(id1, id2, 'a'); const id3 = ts(1, 3); @@ -557,7 +557,7 @@ describe('StringRga', () => { }); test('can merge subsequent ID twice', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'a'); type.ins(ts(1, 2), ts(1, 3), 'bc'); type.ins(ts(1, 4), ts(1, 5), 'def'); @@ -567,7 +567,7 @@ describe('StringRga', () => { test('insert chunk into root with higher ID', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); const id2 = ts(1, 2); type.ins(id1, id2, 'a'); expect(type.view()).toBe('a'); @@ -578,7 +578,7 @@ describe('StringRga', () => { }); test('insert chunk into root with higher ID twice', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'a'); expect(type.view()).toBe('a'); type.ins(ts(1, 1), ts(1, 3), 'bb'); @@ -588,7 +588,7 @@ describe('StringRga', () => { }); test('insert chunk into root with higher ID twice', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'a'); expect(type.view()).toBe('a'); type.ins(ts(1, 1), ts(1, 3), 'bb'); @@ -598,7 +598,7 @@ describe('StringRga', () => { }); test('insert chunk to right of root chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'a'); expect(type.view()).toBe('a'); type.ins(ts(1, 2), ts(1, 22), 'bb'); @@ -608,7 +608,7 @@ describe('StringRga', () => { test('insert in between two chunks', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); const id2 = ts(1, 2); type.ins(id1, id2, 'a'); expect(type.view()).toBe('a'); @@ -622,7 +622,7 @@ describe('StringRga', () => { test('insert in between two chunks twice', () => { const id1 = ts(1, 1); - const type = new StringRga(id1); + const type = new StrNode(id1); const id2 = ts(1, 2); type.ins(id1, id2, 'a'); expect(type.view()).toBe('a'); @@ -637,7 +637,7 @@ describe('StringRga', () => { }); test('can split a chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'aaa'); type.ins(ts(1, 2), ts(1, 666), '!'); expect(type.view()).toBe('a!aa'); @@ -645,7 +645,7 @@ describe('StringRga', () => { }); test('can split a chunk trice', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 4), ts(1, 222), '!'); expect(type.view()).toBe('123!456'); @@ -657,7 +657,7 @@ describe('StringRga', () => { }); test('can insert at root many times', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 1), ts(1, 111), 'aaa'); expect(type.view()).toBe('aaa123456'); @@ -674,7 +674,7 @@ describe('StringRga', () => { }); test('insert after same chunk many times', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 4), ts(1, 111), '...'); expect(type.view()).toBe('123...456'); @@ -691,7 +691,7 @@ describe('StringRga', () => { }); test('append to end of string many times', () => { - const type = new StringRga(ts(123, 10)); + const type = new StrNode(ts(123, 10)); type.ins(ts(123, 10), ts(222, 20), 'a'); expect(type.view()).toBe('a'); assetTreeIsValid(type); @@ -716,7 +716,7 @@ describe('StringRga', () => { }); test('can do various inserts', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 4), ts(1, 222), '!'); expect(type.view()).toBe('123!456'); @@ -742,7 +742,7 @@ describe('StringRga', () => { }); test('inserting same operation twice is idempotent', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 4), ts(1, 111), '...'); type.ins(ts(1, 4), ts(1, 111), '...'); @@ -754,7 +754,7 @@ describe('StringRga', () => { }); test('inserting at root is idempotent', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 1), ts(1, 2), '123456'); @@ -763,7 +763,7 @@ describe('StringRga', () => { }); test('inserting same operation twice is idempotent, when original chunk was merged', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); type.ins(ts(1, 4), ts(1, 111), '...'); type.ins(ts(1, 113), ts(1, 114), '...'); @@ -776,14 +776,14 @@ describe('StringRga', () => { }); test('ignores insert at non-existing position', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 10), '012345'); type.ins(ts(1, 20), ts(1, 333), '|'); expect(type.view()).toBe('012345'); }); test('can insert at the end of the same string twice', () => { - const str = new StringRga(ts(1, 1)); + const str = new StrNode(ts(1, 1)); str.ins(ts(1, 1), ts(1, 2), '12'); assetTreeIsValid(str); expect(str.view()).toBe('12'); @@ -796,7 +796,7 @@ describe('StringRga', () => { }); test('keeps track of split links', () => { - const type = new StringRga(ts(1, 4)); + const type = new StrNode(ts(1, 4)); type.ins(ts(1, 4), ts(1, 10), '012345'); type.ins(ts(1, 10), ts(1, 22), '|'); type.ins(ts(1, 13), ts(1, 33), '|'); @@ -809,7 +809,7 @@ describe('StringRga', () => { }); test('can insert twice at root and once in the middle of another chunk', () => { - const rga = new StringRga(ts(1, 1)); + const rga = new StrNode(ts(1, 1)); rga.ins(ts(1, 1), ts(5, 2), 'ccccc'); assetTreeIsValid(rga); rga.ins(ts(1, 1), ts(4, 2), 'aaaaaaa'); @@ -820,9 +820,9 @@ describe('StringRga', () => { }); test('can insert and read back a chunk by ID', () => { - const type = new StringRga(ts(1, 1)); - type.insertId(new StringChunk(ts(4, 2), 'aaaa'.length, 'aaaa')); - type.insertId(new StringChunk(ts(5, 2), 'aaaa'.length, 'aaaa')); + const type = new StrNode(ts(1, 1)); + type.insertId(new StrChunk(ts(4, 2), 'aaaa'.length, 'aaaa')); + type.insertId(new StrChunk(ts(5, 2), 'aaaa'.length, 'aaaa')); const pair1 = type.findById(ts(4, 2))!; const pair2 = type.findById(ts(4, 4))!; expect(pair1.id).toStrictEqual(ts(4, 2)); @@ -830,12 +830,12 @@ describe('StringRga', () => { }); test('inserting concurrently at the root', () => { - const rga1 = new StringRga(ts(1, 0)); + const rga1 = new StrNode(ts(1, 0)); rga1.ins(ts(1, 0), ts(3, 2), '1'); rga1.ins(ts(3, 2), ts(3, 3), '2'); rga1.ins(ts(3, 2), ts(3, 4), '3'); rga1.ins(ts(1, 0), ts(2, 2), '4'); - const rga2 = new StringRga(ts(1, 0)); + const rga2 = new StrNode(ts(1, 0)); rga2.ins(ts(1, 0), ts(2, 2), '4'); rga2.ins(ts(1, 0), ts(3, 2), '1'); rga2.ins(ts(3, 2), ts(3, 3), '2'); @@ -847,14 +847,14 @@ describe('StringRga', () => { describe('.insAt()', () => { test('can insert into empty string', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'abc'); assetTreeIsValid(type); expect(type.view()).toBe('abc'); }); test('can insert at the beginning of string', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'abc'); assetTreeIsValid(type); type.insAt(0, ts(1, 66), '.'); @@ -866,7 +866,7 @@ describe('StringRga', () => { }); test('can insert at the end of string with sequential ID', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'a'); assetTreeIsValid(type); expect(type.view()).toBe('a'); @@ -876,7 +876,7 @@ describe('StringRga', () => { }); test('can insert at the end of string with ID jump', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'a'); assetTreeIsValid(type); expect(type.view()).toBe('a'); @@ -886,7 +886,7 @@ describe('StringRga', () => { }); test('can insert in the middle of string', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'abc'); assetTreeIsValid(type); expect(type.view()).toBe('abc'); @@ -896,7 +896,7 @@ describe('StringRga', () => { }); test('can insert in the middle of string twice', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'abc'); assetTreeIsValid(type); expect(type.view()).toBe('abc'); @@ -912,7 +912,7 @@ describe('StringRga', () => { }); test('insert many times at the end', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), '1'); assetTreeIsValid(type); expect(type.view()).toBe('1'); @@ -937,7 +937,7 @@ describe('StringRga', () => { }); test('inserting at the same position', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), 'abcd'); assetTreeIsValid(type); expect(type.view()).toBe('abcd'); @@ -958,7 +958,7 @@ describe('StringRga', () => { describe('.delete()', () => { test('can delete a character at beginning', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); type.delete([tss(1, 2, 1)]); @@ -969,7 +969,7 @@ describe('StringRga', () => { }); test('can delete a character in the middle', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); type.delete([tss(1, 3, 1)]); @@ -990,7 +990,7 @@ describe('StringRga', () => { }); test('insert in the middle of a tombstone, and then after each part of split tombstone', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '1234'); assetTreeIsValid(type); expect(type.view()).toBe('1234'); @@ -1009,7 +1009,7 @@ describe('StringRga', () => { }); test('can delete 3 chars', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '12345'); assetTreeIsValid(type); expect(type.view()).toBe('12345'); @@ -1019,7 +1019,7 @@ describe('StringRga', () => { }); test('can delete right across a split-in-the-middle delete', () => { - const type = new StringRga(ts(123456789, 1)); + const type = new StrNode(ts(123456789, 1)); type.ins(ts(123456789, 1), ts(1, 2), '12345'); assetTreeIsValid(type); expect(type.view()).toBe('12345'); @@ -1032,14 +1032,14 @@ describe('StringRga', () => { }); test('can delete right after split-in-the-middle delete, in cloned RGA', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '12345'); assetTreeIsValid(type); expect(type.view()).toBe('12345'); type.delete([tss(1, 4, 1)]); assetTreeIsValid(type); expect(type.view()).toBe('1245'); - const type2 = new StringRga(ts(1, 1)); + const type2 = new StrNode(ts(1, 1)); let curr = type.first(); type2.ingest(type.size(), () => { const res = curr!.clone(); @@ -1058,7 +1058,7 @@ describe('StringRga', () => { }); test('can delete right after split-in-the-middle insert', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '12345'); assetTreeIsValid(type); expect(type.view()).toBe('12345'); @@ -1071,7 +1071,7 @@ describe('StringRga', () => { }); test('can delete after split insert and split delete', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); expect(type.view()).toBe('123456'); @@ -1089,7 +1089,7 @@ describe('StringRga', () => { describe('.find()', () => { test('can find content in a single chunk RGA', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); }); @@ -1097,7 +1097,7 @@ describe('StringRga', () => { describe('.findInterval()', () => { test('can find interval in a single chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); expect(type.findInterval(0, 1)).toStrictEqual([tss(1, 2, 1)]); @@ -1120,7 +1120,7 @@ describe('StringRga', () => { }); test('can find interval across two chunks', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '1234'); type.ins(ts(1, 5), ts(1, 33), '5678'); assetTreeIsValid(type); @@ -1137,7 +1137,7 @@ describe('StringRga', () => { }); test('can select over deletion ranges', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '12345678'); assetTreeIsValid(type); const interval = type.findInterval(3, 2); @@ -1167,7 +1167,7 @@ describe('StringRga', () => { describe('.findInterval2()', () => { test('can clone an RGA', () => { - const type1 = new StringRga(ts(1, 1)); + const type1 = new StrNode(ts(1, 1)); type1.insAt(0, ts(1, 2), '12345'); type1.insAt(3, ts(3, 2), '1234DF678'); type1.insAt(7, ts(3, 22), '12aaaadf678'); @@ -1193,7 +1193,7 @@ describe('StringRga', () => { describe('.pos()', () => { test('returns correct position of a single chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); const chunk = type.first(); @@ -1202,7 +1202,7 @@ describe('StringRga', () => { }); test('returns correct position for three chunks', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), '123456'); assetTreeIsValid(type); type.ins(ts(1, 4), ts(1, 11), '789'); @@ -1216,7 +1216,7 @@ describe('StringRga', () => { }); test('returns correct position for edited text', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'wworld'); assetTreeIsValid(type); type.ins(ts(1, 1), ts(1, 11), 'helo '); @@ -1231,7 +1231,7 @@ describe('StringRga', () => { }); test('check all chunk positions', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 2), 'wworld'); type.ins(ts(1, 1), ts(1, 11), 'helo '); type.ins(ts(1, 12), ts(1, 22), 'l'); @@ -1254,7 +1254,7 @@ describe('StringRga', () => { }); test('calculates correctly position when tombstones present', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.ins(ts(1, 1), ts(1, 7), 'xxx'); type.ins(ts(1, 9), ts(1, 10), 'AA'); type.ins(ts(1, 1), ts(1, 38), 'n'); @@ -1273,7 +1273,7 @@ describe('StringRga', () => { describe('export / import', () => { type Entry = [ITimestampStruct, number, string]; - const exp = (type: StringRga) => { + const exp = (type: StrNode) => { const data: Entry[] = []; for (const chunk of chunks(type)) { data.push([chunk.id, chunk.span, chunk.data || '']); @@ -1282,14 +1282,14 @@ describe('StringRga', () => { }; test('can ingest balanced binary tree from iterator', () => { - const type1 = new StringRga(ts(1, 1)); + const type1 = new StrNode(ts(1, 1)); const verifyExportImport = () => { const data = exp(type1); - const type2 = new StringRga(ts(1, 1)); + const type2 = new StrNode(ts(1, 1)); let i = 0; type2.ingest(data.length, () => { const [id, span, content] = data[i++]; - return new StringChunk(id, span, content); + return new StrChunk(id, span, content); }); assetTreeIsValid(type1); assetTreeIsValid(type2); @@ -1319,7 +1319,7 @@ describe('StringRga', () => { }); test('can clone an RGA', () => { - const type1 = new StringRga(ts(1, 1)); + const type1 = new StrNode(ts(1, 1)); type1.insAt(0, ts(1, 2), '12345678'); type1.insAt(3, ts(3, 2), '12345678'); type1.insAt(7, ts(3, 22), '12345678'); @@ -1329,7 +1329,7 @@ describe('StringRga', () => { type1.delete(type1.findInterval(4, 4)); type1.delete(type1.findInterval(0, 3)); type1.delete(type1.findInterval(5, 1)); - const type2 = new StringRga(ts(1, 1)); + const type2 = new StrNode(ts(1, 1)); const iterator = chunks(type1); type2.ingest(type1.size(), () => { const chunk = iterator.next().value; @@ -1346,7 +1346,7 @@ describe('StringRga', () => { describe('.range0()', () => { test('can clone an RGA', () => { - const type1 = new StringRga(ts(1, 1)); + const type1 = new StrNode(ts(1, 1)); type1.insAt(0, ts(1, 2), '12345'); type1.insAt(3, ts(3, 2), '1234DF678'); type1.insAt(7, ts(3, 22), '12aaaadf678'); @@ -1373,7 +1373,7 @@ describe('StringRga', () => { }); test('can stop iteration at deleted chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), '123456'); type.delete([tss(1, 4, 2)]); const from = ts(1, 2); @@ -1386,7 +1386,7 @@ describe('StringRga', () => { }); test('can start iteration at deleted chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), '123456'); type.delete([tss(1, 4, 2)]); const from = ts(1, 4); @@ -1399,7 +1399,7 @@ describe('StringRga', () => { }); test('does not iterate when from and to are in deleted chunk', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); type.insAt(0, ts(1, 2), '123456'); type.delete([tss(1, 3, 3)]); const from = ts(1, 3); @@ -1412,7 +1412,7 @@ describe('StringRga', () => { }); test('all possible combinations of range computation across deleted chunks', () => { - const type = new StringRga(ts(1, 1)); + const type = new StrNode(ts(1, 1)); const subject = '1234abcd5678efgh'; type.insAt(0, ts(1, 2), '1234abcd5678efgh'); type.delete([tss(1, 6, 4), tss(1, 14, 4)]); @@ -1441,7 +1441,7 @@ describe('StringRga', () => { describe('scenarios', () => { test('couple users editing existing document', () => { - const rga1 = new StringRga(ts(3, 0)); + const rga1 = new StrNode(ts(3, 0)); rga1.ins(ts(3, 0), ts(1, 2), 'bbbbbbbbbbbbbb'); rga1.ins(ts(3, 0), ts(1, 30), 'aaaaaaaaa'); rga1.delete([tss(1, 2, 14)]); @@ -1453,7 +1453,7 @@ describe('StringRga', () => { rga1.ins(ts(1, 31), ts(4, 39), 'eeeeeeeeeeeee'); rga1.delete([tss(1, 30, 22)]); - const rga2 = new StringRga(ts(3, 0)); + const rga2 = new StrNode(ts(3, 0)); rga2.ins(ts(3, 0), ts(1, 2), 'bbbbbbbbbbbbbb'); rga2.ins(ts(3, 0), ts(1, 30), 'aaaaaaaaa'); rga2.delete([tss(1, 2, 14)]); @@ -1470,14 +1470,14 @@ describe('StringRga', () => { }); test('combines deleted and newly inserted chunks with split link, if necessary', () => { - const rga1 = new StringRga(ts(3, 0)); + const rga1 = new StrNode(ts(3, 0)); rga1.ins(ts(3, 0), ts(1, 1), 'aaa'); rga1.ins(ts(1, 3), ts(1, 4), 'bb'); rga1.ins(ts(1, 5), ts(1, 6), 'cc'); rga1.delete([tss(1, 3, 3)]); rga1.delete([tss(1, 2, 5)]); expect(rga1.view()).toBe('ac'); - const rga2 = new StringRga(ts(3, 0)); + const rga2 = new StrNode(ts(3, 0)); rga2.ins(ts(3, 0), ts(1, 1), 'aaa'); rga2.ins(ts(1, 3), ts(1, 4), 'bb'); rga2.delete([tss(1, 3, 3)]); @@ -1492,13 +1492,13 @@ describe('StringRga', () => { }); test('append inserts by concurrent users', () => { - const rga1 = new StringRga(ts(1, 0)); + const rga1 = new StrNode(ts(1, 0)); rga1.ins(ts(1, 0), ts(1, 1), 'a'); rga1.ins(ts(1, 1), ts(1, 2), 'a'); rga1.ins(ts(1, 2), ts(1, 3), '1'); rga1.ins(ts(1, 2), ts(2, 3), '2'); expect(rga1.view()).toBe('aa21'); - const rga2 = new StringRga(ts(1, 0)); + const rga2 = new StrNode(ts(1, 0)); rga2.ins(ts(1, 0), ts(1, 1), 'a'); rga2.ins(ts(1, 1), ts(1, 2), 'a'); rga2.ins(ts(1, 2), ts(2, 3), '2'); @@ -1507,12 +1507,12 @@ describe('StringRga', () => { }); test('one user merging chunk, while another synchronously inserting at the same position', () => { - const rga1 = new StringRga(ts(1, 0)); + const rga1 = new StrNode(ts(1, 0)); rga1.ins(ts(1, 0), ts(1, 1), 'a'); rga1.ins(ts(1, 1), ts(1, 2), '1'); rga1.ins(ts(1, 1), ts(2, 2), '2'); expect(rga1.view()).toBe('a21'); - const rga2 = new StringRga(ts(1, 0)); + const rga2 = new StrNode(ts(1, 0)); rga2.ins(ts(1, 0), ts(1, 1), 'a'); rga2.ins(ts(1, 1), ts(2, 2), '2'); rga2.ins(ts(1, 1), ts(1, 2), '1'); @@ -1520,12 +1520,12 @@ describe('StringRga', () => { }); test('one user merging chunk, while another synchronously inserting at the same position - 2', () => { - const rga1 = new StringRga(ts(1, 0)); + const rga1 = new StrNode(ts(1, 0)); rga1.ins(ts(1, 0), ts(2, 1), 'a'); rga1.ins(ts(2, 1), ts(2, 2), '12345'); rga1.ins(ts(2, 1), ts(1, 2), 'x'); expect(rga1.view()).toBe('a12345x'); - const rga2 = new StringRga(ts(1, 0)); + const rga2 = new StrNode(ts(1, 0)); rga2.ins(ts(1, 0), ts(2, 1), 'a'); rga2.ins(ts(2, 1), ts(1, 2), 'x'); rga2.ins(ts(2, 1), ts(2, 2), '12345'); @@ -1533,14 +1533,14 @@ describe('StringRga', () => { }); test('one user merging chunk, while another synchronously inserting at the same position - 2', () => { - const rga1 = new StringRga(ts(5, 0)); + const rga1 = new StrNode(ts(5, 0)); rga1.ins(ts(5, 0), ts(4, 5), 'YYYYYYYYYYY'); rga1.ins(ts(5, 0), ts(4, 16), 'BBBBBBBBB'); rga1.delete([tss(4, 6, 4)]); rga1.ins(ts(4, 11), ts(7, 81), 'AAAAAAAAAAAA'); rga1.delete([tss(4, 10, 6)]); rga1.delete([tss(4, 5, 3)]); - const rga2 = new StringRga(ts(5, 0)); + const rga2 = new StrNode(ts(5, 0)); rga2.ins(ts(5, 0), ts(4, 5), 'YYYYYYYYYYY'); rga2.ins(ts(5, 0), ts(4, 16), 'BBBBBBBBB'); rga2.delete([tss(4, 10, 6)]); @@ -1551,8 +1551,8 @@ describe('StringRga', () => { }); test('fuzzer bug - do not set split link on a throway chunk', () => { - const rga1 = new StringRga(ts(1, 0)); - const rga2 = new StringRga(ts(1, 0)); + const rga1 = new StrNode(ts(1, 0)); + const rga2 = new StrNode(ts(1, 0)); rga1.ins(ts(1, 0), ts(100, 1), '\\['); rga2.ins(ts(1, 0), ts(100, 1), '\\['); rga1.ins(ts(1, 0), ts(200, 1), '____'); @@ -1583,7 +1583,7 @@ describe('StringRga', () => { // describe('events', () => { // test('calls .onchange on inserts and deletes', () => { - // const rga1 = new StringRga(ts(3, 0)); + // const rga1 = new StrNode(ts(3, 0)); // let cnt = 0; // rga1.onchange = () => cnt++; // rga1.ins(ts(3, 0), ts(1, 2), 'bbbbbbbbbbbbbb'); diff --git a/src/json-crdt/types/rga-string/__tests__/StringRgaFuzzer.ts b/src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts similarity index 90% rename from src/json-crdt/types/rga-string/__tests__/StringRgaFuzzer.ts rename to src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts index 18d6e6d717..3ec450e8ee 100644 --- a/src/json-crdt/types/rga-string/__tests__/StringRgaFuzzer.ts +++ b/src/json-crdt/nodes/str/__tests__/StrNodeFuzzer.ts @@ -2,7 +2,7 @@ import {equal} from 'assert'; import {ITimespanStruct, ITimestampStruct, VectorClock, toDisplayString, ts} from '../../../../json-crdt-patch/clock'; import {Fuzzer} from '../../../../util/Fuzzer'; import {randomSessionId} from '../../../model/util'; -import {StringRga} from '../StringRga'; +import {StrNode} from '../StrNode'; import {printTree} from '../../../../util/print/printTree'; import {Printable} from '../../../../util/print/types'; @@ -36,13 +36,13 @@ interface OpDelete { type Op = OpInsert | OpDelete; -class StringRgaSite implements Printable { - public readonly rga: StringRga; +class StrNodeSite implements Printable { + public readonly rga: StrNode; public readonly clock = new VectorClock(randomSessionId(), 0); public readonly patches: Op[][] = []; - constructor(protected readonly fuzzer: StringRgaFuzzer) { - this.rga = new StringRga(fuzzer.str.id); + constructor(protected readonly fuzzer: StrNodeFuzzer) { + this.rga = new StrNode(fuzzer.str.id); this.apply(fuzzer.prelude); } @@ -102,7 +102,7 @@ class StringRgaSite implements Printable { } } -export interface StringRgaFuzzerOptions { +export interface StrNodeFuzzerOptions { /** * Minimum number of operations in the prelude, * before parallel editing sessions. @@ -169,13 +169,13 @@ export interface StringRgaFuzzerOptions { maxInsertLength: number; } -export class StringRgaFuzzer extends Fuzzer implements Printable { - public readonly str = new StringRga(ts(randomSessionId(), 0)); +export class StrNodeFuzzer extends Fuzzer implements Printable { + public readonly str = new StrNode(ts(randomSessionId(), 0)); public prelude: Op[] = []; - public readonly sites: StringRgaSite[] = [new StringRgaSite(this)]; - public readonly options: StringRgaFuzzerOptions; + public readonly sites: StrNodeSite[] = [new StrNodeSite(this)]; + public readonly options: StrNodeFuzzerOptions; - constructor(opts: Partial = {}, seed?: Buffer) { + constructor(opts: Partial = {}, seed?: Buffer) { super(seed); this.options = { minPreludeLength: 0, @@ -203,7 +203,7 @@ export class StringRgaFuzzer extends Fuzzer implements Printable { this.prelude = this.sites[0].randomPatch(this.options.minPreludeLength, this.options.maxPreludeLength); this.sites[0].apply(this.prelude); const peerCount = this.randomInt(this.options.minSiteCount, this.options.maxSiteCount) - 1; - for (let i = 0; i < peerCount; i++) this.sites.push(new StringRgaSite(this)); + for (let i = 0; i < peerCount; i++) this.sites.push(new StrNodeSite(this)); } public assertSiteViewsEqual(): void { diff --git a/src/json-crdt/types/rga-string/__tests__/sequential-traces.spec.ts b/src/json-crdt/nodes/str/__tests__/sequential-traces.spec.ts similarity index 91% rename from src/json-crdt/types/rga-string/__tests__/sequential-traces.spec.ts rename to src/json-crdt/nodes/str/__tests__/sequential-traces.spec.ts index 05ccc772fb..3be76af5ff 100644 --- a/src/json-crdt/types/rga-string/__tests__/sequential-traces.spec.ts +++ b/src/json-crdt/nodes/str/__tests__/sequential-traces.spec.ts @@ -3,7 +3,7 @@ import {sequentialTraceNames, traces} from '../../../__bench__/util/traces'; import {editors} from '../../../__bench__/util/editors'; describe('can correctly execute sequential traces', () => { - const editor = editors['StringRga (json-joy)']; + const editor = editors['StrNode (json-joy)']; for (const traceName of sequentialTraceNames) { test(`"${traceName}" trace`, async () => { const trace = traces.get(traceName); diff --git a/src/json-crdt/types/types.ts b/src/json-crdt/nodes/types.ts similarity index 85% rename from src/json-crdt/types/types.ts rename to src/json-crdt/nodes/types.ts index 94bdef8133..3d03a30a86 100644 --- a/src/json-crdt/types/types.ts +++ b/src/json-crdt/nodes/types.ts @@ -45,17 +45,17 @@ export type JsonNodeView = N extends JsonNode ? V : {[K in keyof N]: // prettier-ignore export type BuilderNodeToJsonNode = S extends builder.str - ? nodes.StringRga + ? nodes.StrNode : S extends builder.bin - ? nodes.BinaryRga + ? nodes.BinNode : S extends builder.con - ? nodes.Const + ? nodes.ConNode : S extends builder.val - ? nodes.ValueLww> + ? nodes.ValNode> : S extends builder.vec - ? nodes.ArrayLww<{[K in keyof T]: BuilderNodeToJsonNode}> + ? nodes.VecNode<{[K in keyof T]: BuilderNodeToJsonNode}> : S extends builder.obj - ? nodes.ObjectLww<{[K in keyof T]: BuilderNodeToJsonNode}> + ? nodes.ObjNode<{[K in keyof T]: BuilderNodeToJsonNode}> : S extends builder.arr - ? nodes.ArrayRga> + ? nodes.ArrNode> : JsonNode; diff --git a/src/json-crdt/types/lww-value/ValueLww.ts b/src/json-crdt/nodes/val/ValNode.ts similarity index 89% rename from src/json-crdt/types/lww-value/ValueLww.ts rename to src/json-crdt/nodes/val/ValNode.ts index 5015285b0c..6761c286d8 100644 --- a/src/json-crdt/types/lww-value/ValueLww.ts +++ b/src/json-crdt/nodes/val/ValNode.ts @@ -1,7 +1,7 @@ import {compare, ITimestampStruct, toDisplayString} from '../../../json-crdt-patch/clock'; import {SESSION} from '../../../json-crdt-patch/constants'; import {printTree} from '../../../util/print/printTree'; -import type {JsonNode, JsonNodeView} from '../../types'; +import type {JsonNode, JsonNodeView} from '..'; import type {Model} from '../../model'; import type {Printable} from '../../../util/print/types'; @@ -12,7 +12,7 @@ import type {Printable} from '../../../util/print/types'; * * @category CRDT Node */ -export class ValueLww implements JsonNode>>, Printable { +export class ValNode implements JsonNode>>, Printable { constructor( /** * @ignore @@ -83,7 +83,7 @@ export class ValueLww implements JsonNode (node ? node.toString(tab) : toDisplayString(this.val))]); } } diff --git a/src/json-crdt/types/lww-array/ArrayLww.ts b/src/json-crdt/nodes/vec/VecNode.ts similarity index 96% rename from src/json-crdt/types/lww-array/ArrayLww.ts rename to src/json-crdt/nodes/vec/VecNode.ts index 9737faef37..cbd4bf0a1e 100644 --- a/src/json-crdt/types/lww-array/ArrayLww.ts +++ b/src/json-crdt/nodes/vec/VecNode.ts @@ -1,9 +1,9 @@ -import {Const} from '../const/Const'; +import {ConNode} from '../con/ConNode'; import {CRDT_CONSTANTS} from '../../constants'; import {printTree} from '../../../util/print/printTree'; import {compare, ITimestampStruct, toDisplayString} from '../../../json-crdt-patch/clock'; import type {Model} from '../../model'; -import type {JsonNode, JsonNodeView} from '../../types'; +import type {JsonNode, JsonNodeView} from '..'; import type {Printable} from '../../../util/print/types'; /** @@ -15,7 +15,7 @@ import type {Printable} from '../../../util/print/types'; * * @category CRDT Node */ -export class ArrayLww +export class VecNode implements JsonNode>>, Printable { /** @@ -98,7 +98,7 @@ export class ArrayLww public getExtId(): number { if (this.elements.length !== 2) return -1; const type = this.get(0); - if (!(type instanceof Const)) return -1; + if (!(type instanceof ConNode)) return -1; const buf = type.val; const id = this.id; if (!(buf instanceof Uint8Array) || buf.length !== 3 || buf[1] !== id.sid % 256 || buf[2] !== id.time % 256) diff --git a/src/json-crdt/types/__tests__/types.spec.ts b/src/json-crdt/types/__tests__/types.spec.ts deleted file mode 100644 index 0f7bc4d33a..0000000000 --- a/src/json-crdt/types/__tests__/types.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type {Const} from '../const/Const'; -import type {ArrayLww} from '../lww-array/ArrayLww'; -import type {ObjectLww} from '../lww-object/ObjectLww'; -import type {ValueLww} from '../lww-value/ValueLww'; -import type {ArrayRga} from '../rga-array/ArrayRga'; -import type {BinaryRga} from '../rga-binary/BinaryRga'; -import type {StringRga} from '../rga-string/StringRga'; -import type {JsonNodeView} from '../types'; - -test('can infer view type of CRDT nodes', () => { - type N1 = Const; - type N2 = Const; - type N3 = Const; - type N4 = Const<{foo: 'bar'}>; - type N5 = ValueLww; - type N6 = ValueLww; - type N7 = ValueLww; - type N8 = StringRga; - type N9 = BinaryRga; - type N10 = ArrayRga; - type N11 = ArrayRga; - type N13 = ArrayLww<[N1, N2, N8, N11]>; - type N14 = ObjectLww<{ - n1: N1; - n2: N2; - n3: N3; - n4: N4; - n5: N5; - n6: N6; - n7: N7; - n8: N8; - n9: N9; - n10: N10; - n11: N11; - n13: N13; - }>; - type View = JsonNodeView; - const a: View = { - n1: 123, - n2: true, - n3: 'foo', - n4: {foo: 'bar'}, - n5: 123, - n6: {foo: 'bar'}, - n7: {foo: 'bar'}, - n8: 'foo', - n9: Uint8Array.from([1, 2, 3]), - n10: [123, 123], - n11: [123, true, 123], - n13: [123, true, 'foo', [123, true, true]], - }; -}); diff --git a/src/json-crdt/types/nodes.ts b/src/json-crdt/types/nodes.ts deleted file mode 100644 index fc2db407bd..0000000000 --- a/src/json-crdt/types/nodes.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type {ITimestampStruct} from '../../json-crdt-patch/clock'; -import type {Const} from './const/Const'; -import type {ValueLww} from './lww-value/ValueLww'; -import type {ArrayLww} from './lww-array/ArrayLww'; -import type {JsonNode} from './types'; -import type {ObjectLww} from './lww-object/ObjectLww'; -import type {StringRga} from './rga-string/StringRga'; -import type {BinaryRga} from './rga-binary/BinaryRga'; -import type {ArrayRga} from './rga-array/ArrayRga'; - -// tslint:disable-next-line:no-namespace -export namespace n { - export type con = Const; - export type val = ValueLww; - export type vec = ArrayLww; - export type obj = Record> = ObjectLww; - export type str = StringRga; - export type bin = BinaryRga; - export type arr = ArrayRga; -} - -export {Const} from './const/Const'; -export {ValueLww} from './lww-value/ValueLww'; -export {RootLww} from './lww-root/RootLww'; -export {ArrayLww} from './lww-array/ArrayLww'; -export {ObjectLww} from './lww-object/ObjectLww'; -export {ArrayRga, ArrayChunk} from './rga-array/ArrayRga'; -export {BinaryRga, BinaryChunk} from './rga-binary/BinaryRga'; -export {StringRga, StringChunk} from './rga-string/StringRga'; diff --git a/src/json-type/codegen/binary/__tests__/testBinaryCodegen.ts b/src/json-type/codegen/binary/__tests__/testBinaryCodegen.ts index f4989df3c8..cb2a6eb241 100644 --- a/src/json-type/codegen/binary/__tests__/testBinaryCodegen.ts +++ b/src/json-type/codegen/binary/__tests__/testBinaryCodegen.ts @@ -1,4 +1,3 @@ -import {EncodingFormat} from '../../../../json-pack/constants'; import {TypeSystem} from '../../../system'; import {Type} from '../../../type';