From 7d363308a7932c220fe04046ef8c6edf76af2716 Mon Sep 17 00:00:00 2001 From: Oak Date: Wed, 18 Sep 2024 17:01:08 +0100 Subject: [PATCH] chore: remove crdt package; add blueprints package (#162) --- examples/canvas/package.json | 2 +- examples/canvas/src/index.ts | 2 +- examples/canvas/src/objects/canvas.ts | 27 +-- examples/canvas/src/objects/pixel.ts | 39 ++--- examples/chat/asconfig.json | 2 +- examples/chat/package.json | 4 +- examples/chat/src/index.ts | 4 +- examples/chat/src/objects/chat.ts | 41 +---- packages/{crdt => blueprints}/README.md | 14 +- packages/{crdt => blueprints}/asconfig.json | 0 packages/blueprints/package.json | 38 ++++ .../src}/AddWinsSet/index.ts | 0 .../src}/PseudoRandomWinsSet/index.ts | 0 packages/blueprints/src/index.asc.ts | 2 + packages/blueprints/src/index.ts | 2 + .../tests/AddWinsSet.test.ts | 2 +- .../tests/PseudoRandomWinsSet.test.ts | 2 +- packages/{crdt => blueprints}/tsconfig.json | 0 packages/{crdt => blueprints}/typedoc.json | 0 packages/crdt/package.json | 43 ----- packages/crdt/src/crdts/2PSet/index.ts | 44 ----- packages/crdt/src/crdts/GCounter/index.ts | 47 ----- packages/crdt/src/crdts/GSet/index.ts | 59 ------- packages/crdt/src/crdts/IPSet/index.ts | 77 -------- .../crdt/src/crdts/LWWElementSet/index.ts | 80 --------- packages/crdt/src/crdts/LWWRegister/index.ts | 47 ----- packages/crdt/src/crdts/OORSet/index.ts | 90 ---------- packages/crdt/src/crdts/PNCounter/index.ts | 44 ----- packages/crdt/src/crdts/RGA/README.md | 50 ------ packages/crdt/src/crdts/RGA/index.ts | 164 ------------------ packages/crdt/src/index.asc.ts | 7 - packages/crdt/src/index.ts | 11 -- packages/crdt/tests/GCounter.test.ts | 42 ----- packages/crdt/tests/GSet.test.ts | 44 ----- packages/crdt/tests/IPSet.test.ts | 145 ---------------- packages/crdt/tests/LWWElementSet.test.ts | 90 ---------- packages/crdt/tests/LWWRegister.test.ts | 64 ------- packages/crdt/tests/OORSet.test.ts | 44 ----- packages/crdt/tests/PNCounter.test.ts | 57 ------ packages/crdt/tests/RGA.test.ts | 91 ---------- packages/crdt/tests/TwoPSet.test.ts | 59 ------- packages/node/package.json | 83 +++++---- packages/node/tsconfig.json | 2 +- packages/object/asconfig.json | 2 +- packages/object/src/wasm/compiler.ts | 4 +- packages/object/tests/hashgraph.test.ts | 6 +- pnpm-lock.yaml | 63 ++++--- 47 files changed, 153 insertions(+), 1587 deletions(-) rename packages/{crdt => blueprints}/README.md (52%) rename packages/{crdt => blueprints}/asconfig.json (100%) create mode 100644 packages/blueprints/package.json rename packages/{crdt/src/cros => blueprints/src}/AddWinsSet/index.ts (100%) rename packages/{crdt/src/cros => blueprints/src}/PseudoRandomWinsSet/index.ts (100%) create mode 100644 packages/blueprints/src/index.asc.ts create mode 100644 packages/blueprints/src/index.ts rename packages/{crdt => blueprints}/tests/AddWinsSet.test.ts (91%) rename packages/{crdt => blueprints}/tests/PseudoRandomWinsSet.test.ts (89%) rename packages/{crdt => blueprints}/tsconfig.json (100%) rename packages/{crdt => blueprints}/typedoc.json (100%) delete mode 100644 packages/crdt/package.json delete mode 100644 packages/crdt/src/crdts/2PSet/index.ts delete mode 100644 packages/crdt/src/crdts/GCounter/index.ts delete mode 100644 packages/crdt/src/crdts/GSet/index.ts delete mode 100644 packages/crdt/src/crdts/IPSet/index.ts delete mode 100644 packages/crdt/src/crdts/LWWElementSet/index.ts delete mode 100644 packages/crdt/src/crdts/LWWRegister/index.ts delete mode 100644 packages/crdt/src/crdts/OORSet/index.ts delete mode 100644 packages/crdt/src/crdts/PNCounter/index.ts delete mode 100644 packages/crdt/src/crdts/RGA/README.md delete mode 100644 packages/crdt/src/crdts/RGA/index.ts delete mode 100644 packages/crdt/src/index.asc.ts delete mode 100644 packages/crdt/src/index.ts delete mode 100644 packages/crdt/tests/GCounter.test.ts delete mode 100644 packages/crdt/tests/GSet.test.ts delete mode 100644 packages/crdt/tests/IPSet.test.ts delete mode 100644 packages/crdt/tests/LWWElementSet.test.ts delete mode 100644 packages/crdt/tests/LWWRegister.test.ts delete mode 100644 packages/crdt/tests/OORSet.test.ts delete mode 100644 packages/crdt/tests/PNCounter.test.ts delete mode 100644 packages/crdt/tests/RGA.test.ts delete mode 100644 packages/crdt/tests/TwoPSet.test.ts diff --git a/examples/canvas/package.json b/examples/canvas/package.json index b29bf671..989ce7ce 100644 --- a/examples/canvas/package.json +++ b/examples/canvas/package.json @@ -9,7 +9,7 @@ "start": "ts-node ./src/index.ts" }, "dependencies": { - "@topology-foundation/crdt": "0.1.1", + "@topology-foundation/blueprints": "0.1.1", "@topology-foundation/network": "0.1.1", "@topology-foundation/node": "0.1.1", "@topology-foundation/object": "0.1.1", diff --git a/examples/canvas/src/index.ts b/examples/canvas/src/index.ts index 31346b82..4d2d4459 100644 --- a/examples/canvas/src/index.ts +++ b/examples/canvas/src/index.ts @@ -45,7 +45,7 @@ function paint_pixel(pixel: HTMLDivElement) { random_int(256), random_int(256), ]; - canvasCRO.paint(node.networkNode.peerId, [x, y], painting); + canvasCRO.paint([x, y], painting); const [r, g, b] = canvasCRO.pixel(x, y).color(); pixel.style.backgroundColor = `rgb(${r}, ${g}, ${b})`; } diff --git a/examples/canvas/src/objects/canvas.ts b/examples/canvas/src/objects/canvas.ts index ec7557da..f568a03d 100644 --- a/examples/canvas/src/objects/canvas.ts +++ b/examples/canvas/src/objects/canvas.ts @@ -24,24 +24,18 @@ export class Canvas implements CRO { } splash( - nodeId: string, offset: [number, number], size: [number, number], rgb: [number, number, number], ): void { - this._splash(nodeId, offset, size, rgb); + this._splash(offset, size, rgb); } - paint( - nodeId: string, - offset: [number, number], - rgb: [number, number, number], - ): void { - this._paint(nodeId, offset, rgb); + paint(offset: [number, number], rgb: [number, number, number]): void { + this._paint(offset, rgb); } private _splash( - nodeId: string, offset: [number, number], size: [number, number], rgb: [number, number, number], @@ -51,32 +45,25 @@ export class Canvas implements CRO { for (let x = offset[0]; x < this.width || x < offset[0] + size[0]; x++) { for (let y = offset[1]; y < this.height || y < offset[1] + size[1]; y++) { - this.canvas[x][y].paint(nodeId, rgb); + this.canvas[x][y].paint(rgb); } } } private _paint( - nodeId: string, offset: [number, number], rgb: [number, number, number], ): void { if (offset[0] < 0 || this.canvas.length < offset[0]) return; if (offset[1] < 0 || this.canvas[offset[0]].length < offset[1]) return; - this.canvas[offset[0]][offset[1]].paint(nodeId, rgb); + this.canvas[offset[0]][offset[1]].paint(rgb); } pixel(x: number, y: number): Pixel { return this.canvas[x][y]; } - merge(peerCanvas: Canvas): void { - this.canvas.forEach((row, x) => - row.forEach((pixel, y) => pixel.merge(peerCanvas.pixel(x, y))), - ); - } - resolveConflicts(_): ResolveConflictsType { return { action: ActionType.Nop }; } @@ -90,12 +77,12 @@ export class Canvas implements CRO { switch (op.type) { case "splash": { const [nodeId, offset, size, rgb] = op.value; - this._splash(nodeId, offset, size, rgb); + this._splash(offset, size, rgb); break; } case "paint": { const [nodeId, offset, rgb] = op.value; - this._paint(nodeId, offset, rgb); + this._paint(offset, rgb); break; } } diff --git a/examples/canvas/src/objects/pixel.ts b/examples/canvas/src/objects/pixel.ts index 38c1c999..18652c87 100644 --- a/examples/canvas/src/objects/pixel.ts +++ b/examples/canvas/src/objects/pixel.ts @@ -1,38 +1,25 @@ -import { GCounter } from "@topology-foundation/crdt"; - export class Pixel { - red: GCounter; - green: GCounter; - blue: GCounter; + red: number; + green: number; + blue: number; - constructor() { - this.red = new GCounter({}); - this.green = new GCounter({}); - this.blue = new GCounter({}); + constructor(red?: number, green?: number, blue?: number) { + this.red = red ?? 0; + this.green = green ?? 0; + this.blue = blue ?? 0; } color(): [number, number, number] { - return [ - this.red.value() % 256, - this.green.value() % 256, - this.blue.value() % 256, - ]; - } - - paint(nodeId: string, rgb: [number, number, number]): void { - this.red.increment(nodeId, rgb[0]); - this.green.increment(nodeId, rgb[1]); - this.blue.increment(nodeId, rgb[2]); + return [this.red % 256, this.green % 256, this.blue % 256]; } - counters(): [GCounter, GCounter, GCounter] { + counters(): [number, number, number] { return [this.red, this.green, this.blue]; } - merge(peerPixel: Pixel): void { - const peerCounters = peerPixel.counters(); - this.red.merge(peerCounters[0]); - this.green.merge(peerCounters[1]); - this.blue.merge(peerCounters[2]); + paint(rgb: [number, number, number]): void { + this.red += rgb[0]; + this.green += rgb[1]; + this.blue += rgb[2]; } } diff --git a/examples/chat/asconfig.json b/examples/chat/asconfig.json index 6ba1bbb0..46e620da 100644 --- a/examples/chat/asconfig.json +++ b/examples/chat/asconfig.json @@ -20,7 +20,7 @@ "options": { "lib": { "@topology-foundation/crdt": [ - "../node_modules/@topology-foundation/crdt/src/index.asc.ts" + "./node_modules/@topology-foundation/blueprints/src/index.asc.ts" ] } } diff --git a/examples/chat/package.json b/examples/chat/package.json index 9fd78ed9..55b77e06 100644 --- a/examples/chat/package.json +++ b/examples/chat/package.json @@ -1,7 +1,7 @@ { "name": "topology-example-chat", "version": "0.1.1", - "description": "Topology Protocol Chat Exmaple", + "description": "Topology Protocol Chat Example", "main": "src/index.ts", "repository": "https://github.com/topology-foundation/ts-topology.git", "license": "MIT", @@ -13,7 +13,7 @@ "start": "ts-node ./src/index.ts" }, "dependencies": { - "@topology-foundation/crdt": "0.1.1", + "@topology-foundation/blueprints": "0.1.1", "@topology-foundation/network": "0.1.1", "@topology-foundation/node": "0.1.1", "@topology-foundation/object": "0.1.1", diff --git a/examples/chat/src/index.ts b/examples/chat/src/index.ts index fe00971e..50e6db6c 100644 --- a/examples/chat/src/index.ts +++ b/examples/chat/src/index.ts @@ -35,14 +35,14 @@ const render = () => { const element_chat = document.getElementById("chat"); element_chat.innerHTML = ""; - if (chat.set.size === 0) { + if (chat.size === 0) { const div = document.createElement("div"); div.innerHTML = "No messages yet"; div.style.padding = "10px"; element_chat.appendChild(div); return; } - for (const message of [...chat.set].sort()) { + for (const message of [...chat].sort()) { const div = document.createElement("div"); div.innerHTML = message; div.style.padding = "10px"; diff --git a/examples/chat/src/objects/chat.ts b/examples/chat/src/objects/chat.ts index b60e43c7..1f3238e9 100644 --- a/examples/chat/src/objects/chat.ts +++ b/examples/chat/src/objects/chat.ts @@ -1,10 +1,3 @@ -// if it can't compile, append src/index.asc to the import path on runtime -import { - type GSet, - gset_add, - gset_create, - gset_merge, -} from "@topology-foundation/crdt"; import { ActionType, type CRO, @@ -18,9 +11,9 @@ export class Chat implements CRO { operations: string[] = ["addMessage"]; semanticsType: SemanticsType = SemanticsType.pair; // store messages as strings in the format (timestamp, message, nodeId) - messages: GSet; + messages: Set; constructor() { - this.messages = gset_create(); + this.messages = new Set(); } addMessage(timestamp: string, message: string, nodeId: string): void { @@ -35,14 +28,10 @@ export class Chat implements CRO { this.messages.add(`(${timestamp}, ${message}, ${nodeId})`); } - getMessages(): GSet { + getMessages(): Set { return this.messages; } - merge(other: Chat): void { - this.messages.merge(other.messages); - } - resolveConflicts(vertices: Vertex[]): ResolveConflictsType { return { action: ActionType.Nop }; } @@ -54,27 +43,3 @@ export class Chat implements CRO { } } } - -export function createChat(): Chat { - return new Chat(); -} - -// @ts-ignore -export function addMessage( - chat: Chat, - timestamp: string, - message: string, - nodeId: string, -): void { - gset_add(chat.messages, `(${timestamp}, ${message}, ${nodeId})`); -} - -// @ts-ignore -export function getMessages(chat: Chat): GSet { - return chat.messages; -} - -// @ts-ignore -export function merge(chat: Chat, other: Chat): void { - gset_merge(chat.messages, other.messages); -} diff --git a/packages/crdt/README.md b/packages/blueprints/README.md similarity index 52% rename from packages/crdt/README.md rename to packages/blueprints/README.md index f0512d57..96cf49e9 100644 --- a/packages/crdt/README.md +++ b/packages/blueprints/README.md @@ -1,6 +1,6 @@ -# Conflict-free Replicated Data Types (CRDTs) +# Topology Blueprints -This package contains the CRDT implementations intended to use as builtins for the Topology Protocol. +This package contains the CRO blueprints intended to be used by other CROs. ## Usage @@ -8,10 +8,10 @@ This package is intended to be used as a dependency for the Topology Protocol. H ```bash # yarn -yarn add @topology-foundation/crdt +yarn add @topology-foundation/blueprints # npm -npm install @topology-foundation/crdt +npm install @topology-foundation/blueprints ``` ### Build @@ -29,9 +29,3 @@ To run the tests, you can run: ```bash yarn test ``` - -## CRDTs Implementations -- [x] G-Counter -- [x] PN-Counter -- [x] G-Set -- [x] 2P-Set diff --git a/packages/crdt/asconfig.json b/packages/blueprints/asconfig.json similarity index 100% rename from packages/crdt/asconfig.json rename to packages/blueprints/asconfig.json diff --git a/packages/blueprints/package.json b/packages/blueprints/package.json new file mode 100644 index 00000000..ed471868 --- /dev/null +++ b/packages/blueprints/package.json @@ -0,0 +1,38 @@ +{ + "name": "@topology-foundation/blueprints", + "version": "0.1.1", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/topology-foundation/ts-topology.git" + }, + "type": "module", + "types": "./dist/src/index.d.ts", + "files": ["src", "dist", "!dist/test", "!**/*.tsbuildinfo"], + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./dist/src/index.js" + }, + "./wasm": { + "types": "./dist/src/index.d.ts", + "import": "./src/index.asc.ts" + } + }, + "scripts": { + "asbuild": "yarn asbuild:debug && yarn asbuild:release", + "asbuild:debug": "asc --config asconfig.json --target debug", + "asbuild:release": "asc --config asconfig.json --target release", + "build": "tsc -b", + "clean": "rm -rf dist/ node_modules/", + "prepack": "tsc -b", + "test": "vitest" + }, + "devDependencies": { + "@topology-foundation/object": "0.1.1", + "assemblyscript": "^0.27.29" + }, + "dependencies": { + "@thi.ng/random": "^4.0.3" + } +} diff --git a/packages/crdt/src/cros/AddWinsSet/index.ts b/packages/blueprints/src/AddWinsSet/index.ts similarity index 100% rename from packages/crdt/src/cros/AddWinsSet/index.ts rename to packages/blueprints/src/AddWinsSet/index.ts diff --git a/packages/crdt/src/cros/PseudoRandomWinsSet/index.ts b/packages/blueprints/src/PseudoRandomWinsSet/index.ts similarity index 100% rename from packages/crdt/src/cros/PseudoRandomWinsSet/index.ts rename to packages/blueprints/src/PseudoRandomWinsSet/index.ts diff --git a/packages/blueprints/src/index.asc.ts b/packages/blueprints/src/index.asc.ts new file mode 100644 index 00000000..8555077c --- /dev/null +++ b/packages/blueprints/src/index.asc.ts @@ -0,0 +1,2 @@ +export * from "./AddWinsSet/index.js"; +export * from "./PseudoRandomWinsSet/index.js"; diff --git a/packages/blueprints/src/index.ts b/packages/blueprints/src/index.ts new file mode 100644 index 00000000..8555077c --- /dev/null +++ b/packages/blueprints/src/index.ts @@ -0,0 +1,2 @@ +export * from "./AddWinsSet/index.js"; +export * from "./PseudoRandomWinsSet/index.js"; diff --git a/packages/crdt/tests/AddWinsSet.test.ts b/packages/blueprints/tests/AddWinsSet.test.ts similarity index 91% rename from packages/crdt/tests/AddWinsSet.test.ts rename to packages/blueprints/tests/AddWinsSet.test.ts index 77340d68..cdd3485f 100644 --- a/packages/crdt/tests/AddWinsSet.test.ts +++ b/packages/blueprints/tests/AddWinsSet.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { AddWinsSet } from "../src/cros/AddWinsSet/index.js"; +import { AddWinsSet } from "../src/AddWinsSet/index.js"; describe("HashGraph for AddWinSet tests", () => { let cro: AddWinsSet; diff --git a/packages/crdt/tests/PseudoRandomWinsSet.test.ts b/packages/blueprints/tests/PseudoRandomWinsSet.test.ts similarity index 89% rename from packages/crdt/tests/PseudoRandomWinsSet.test.ts rename to packages/blueprints/tests/PseudoRandomWinsSet.test.ts index 01d1ed51..d5f96af8 100644 --- a/packages/crdt/tests/PseudoRandomWinsSet.test.ts +++ b/packages/blueprints/tests/PseudoRandomWinsSet.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { PseudoRandomWinsSet } from "../src/cros/PseudoRandomWinsSet/index.js"; +import { PseudoRandomWinsSet } from "../src/PseudoRandomWinsSet/index.js"; describe("HashGraph for PseudoRandomWinsSet tests", () => { let cro: PseudoRandomWinsSet; diff --git a/packages/crdt/tsconfig.json b/packages/blueprints/tsconfig.json similarity index 100% rename from packages/crdt/tsconfig.json rename to packages/blueprints/tsconfig.json diff --git a/packages/crdt/typedoc.json b/packages/blueprints/typedoc.json similarity index 100% rename from packages/crdt/typedoc.json rename to packages/blueprints/typedoc.json diff --git a/packages/crdt/package.json b/packages/crdt/package.json deleted file mode 100644 index c8c05551..00000000 --- a/packages/crdt/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@topology-foundation/crdt", - "version": "0.1.1", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/topology-foundation/ts-topology.git" - }, - "type": "module", - "types": "./dist/src/index.d.ts", - "files": [ - "src", - "dist", - "!dist/test", - "!**/*.tsbuildinfo" - ], - "exports": { - ".": { - "types": "./dist/src/index.d.ts", - "import": "./dist/src/index.js" - }, - "./wasm": { - "types": "./dist/src/index.d.ts", - "import": "./src/index.asc.ts" - } - }, - "scripts": { - "asbuild": "yarn asbuild:debug && yarn asbuild:release", - "asbuild:debug": "asc --config asconfig.json --target debug", - "asbuild:release": "asc --config asconfig.json --target release", - "build": "tsc -b", - "clean": "rm -rf dist/ node_modules/", - "prepack": "tsc -b", - "test": "vitest" - }, - "devDependencies": { - "@topology-foundation/object": "0.1.1", - "assemblyscript": "^0.27.29" - }, - "dependencies": { - "@thi.ng/random": "^4.0.3" - } -} diff --git a/packages/crdt/src/crdts/2PSet/index.ts b/packages/crdt/src/crdts/2PSet/index.ts deleted file mode 100644 index fd63dae6..00000000 --- a/packages/crdt/src/crdts/2PSet/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { GSet } from "../GSet/index.js"; - -/// 2PSet with support for state and op changes -export class TwoPSet { - private _adds: GSet; - private _removes: GSet; - - constructor(adds: GSet, removes: GSet) { - this._adds = adds; - this._removes = removes; - } - - lookup(element: T): boolean { - return this._adds.lookup(element) && !this._removes.lookup(element); - } - - add(element: T): void { - this._adds.add(element); - } - - remove(element: T): void { - this._removes.add(element); - } - - adds(): GSet { - return this._adds; - } - - removes(): GSet { - return this._removes; - } - - compare(peerSet: TwoPSet): boolean { - return ( - this._adds.compare(peerSet.adds()) && - this._removes.compare(peerSet.removes()) - ); - } - - merge(peerSet: TwoPSet): void { - this._adds.merge(peerSet.adds()); - this._removes.merge(peerSet.removes()); - } -} diff --git a/packages/crdt/src/crdts/GCounter/index.ts b/packages/crdt/src/crdts/GCounter/index.ts deleted file mode 100644 index 932a14f6..00000000 --- a/packages/crdt/src/crdts/GCounter/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -/// GCounter with support for state and op changes -export class GCounter { - globalCounter: number; - // instead of standard incremental id for replicas - // we map the counter with the node id - counts: { [nodeId: string]: number }; - - constructor(counts: { [nodeId: string]: number }) { - this.globalCounter = Object.values(counts).reduce((a, b) => a + b, 0); - this.counts = counts; - } - - value(): number { - return this.globalCounter; - } - - increment(nodeId: string, amount: number): void { - this.globalCounter += amount; - this.counts[nodeId] += amount; - } - - compare(peerCounter: GCounter): boolean { - return ( - this.counts.length === peerCounter.counts.length && - Object.keys(this.counts).every( - (key) => this.counts[key] <= peerCounter.counts[key], - ) - ); - } - - merge(peerCounter: GCounter): void { - const temp: { [nodeKey: string]: number } = Object.assign( - {}, - this.counts, - peerCounter.counts, - ); - - for (const key of Object.keys(temp)) { - this.counts[key] = Math.max( - this.counts[key] || 0, - peerCounter.counts[key] || 0, - ); - } - - this.globalCounter = Object.values(this.counts).reduce((a, b) => a + b, 0); - } -} diff --git a/packages/crdt/src/crdts/GSet/index.ts b/packages/crdt/src/crdts/GSet/index.ts deleted file mode 100644 index 9ed4eb92..00000000 --- a/packages/crdt/src/crdts/GSet/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* GSet with support for state and op changes */ -export class GSet { - set: Set; - - constructor(set: Set = new Set()) { - this.set = set; - } - - add(element: T): void { - this.set.add(element); - } - - lookup(element: T): boolean { - return this.set.has(element); - } - - compare(peerSet: GSet): boolean { - return ( - this.set.size === peerSet.set.size && - [...this.set].every((value) => peerSet.set.has(value)) - ); - } - - merge(peerSet: GSet): void { - this.set = new Set([...this.set, ...peerSet.set]); - } -} - -/// AssemblyScript functions -export function gset_create(set: Set = new Set()): GSet { - return new GSet(set); -} - -export function gset_add(gset: GSet, element: T): void { - gset.add(element); -} - -export function gset_lookup(gset: GSet, element: T): boolean { - return gset.lookup(element); -} - -export function gset_compare(gset: GSet, peerSet: GSet): boolean { - return ( - gset.set.size === peerSet.set.size && - gset.set - .values() - // @ts-ignore - .every((value) => peerSet.set.has(value)) - ); -} - -export function gset_merge(gset: GSet, peerSet: GSet): void { - const set = gset.set.values(); - // @ts-ignore - for (let i = 0, l = peerSet.set.values().length; i < l; ++i) { - // @ts-ignore - gset.add(set[i]); - } -} diff --git a/packages/crdt/src/crdts/IPSet/index.ts b/packages/crdt/src/crdts/IPSet/index.ts deleted file mode 100644 index 2148a515..00000000 --- a/packages/crdt/src/crdts/IPSet/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { GCounter } from "../GCounter/index.js"; - -/// State-based infinite-phase set (IPSet) -export class IPSet { - // Grow-only mapping of elements to GCounters - private _counters: Map; - - // State: - // - an element exists in the IPSet if _counter[element] exists and is an odd number - // - otherwise the element doesn't exist in the IPSet - - constructor(counters: Map = new Map()) { - this._counters = counters; - } - - counters(): Map { - return this._counters; - } - - add(nodeId: string, element: T): void { - if (!this._counters.has(element)) { - this._counters.set(element, new GCounter({ [nodeId]: 1 })); - } else if ((this._counters.get(element)?.value() ?? 0) % 2 === 0) { - this._counters.get(element)?.increment(nodeId, 1); - } - } - - remove(nodeId: string, element: T): void { - if ( - this._counters.has(element) && - (this._counters.get(element)?.value() ?? 0) % 2 === 1 - ) { - this._counters.get(element)?.increment(nodeId, 1); - } - } - - contains(element: T): boolean { - if (this._counters.has(element)) { - return (this._counters.get(element)?.value() ?? 0) % 2 === 1; - } - return false; - } - - set(): Set { - const result = new Set(); - for (const [element, counter] of this._counters.entries()) { - if (counter.value() % 2 === 1) { - result.add(element); - } - } - return result; - } - - compare(peerSet: IPSet): boolean { - // Returns true if peerSet includes all operations that were performed on the given IPSet and possibly more. - // this._counters has to be a subset of peerSet._counters - // and for each element, the value of the counter in this._counters has to be less than or equal to the value of the counter in peerSet._counters - return [...this._counters.keys()].every( - (element) => - peerSet.counters().has(element) && - (this._counters.get(element)?.value() ?? 0) <= - (peerSet.counters().get(element)?.value() ?? 0), - ); - } - - merge(peerSet: IPSet): void { - for (const [element, counter] of peerSet._counters.entries()) { - // if element is not in local replica, set local counter for element to counter - // otherwise, merge the counters - if (!this._counters.has(element)) { - this._counters.set(element, counter); - } else { - this._counters.get(element)?.merge(counter); - } - } - } -} diff --git a/packages/crdt/src/crdts/LWWElementSet/index.ts b/packages/crdt/src/crdts/LWWElementSet/index.ts deleted file mode 100644 index a94d48cd..00000000 --- a/packages/crdt/src/crdts/LWWElementSet/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -export enum Bias { - ADD = 0, - REMOVE = 1, -} - -export class LWWElementSet { - private _adds: Map; - private _removes: Map; - public _bias: Bias; - - constructor(adds: Map, removes: Map, bias: Bias) { - this._adds = adds; - this._removes = removes; - this._bias = bias; - } - - lookup(element: T): boolean { - const addTimestamp = this._adds.get(element); - if (addTimestamp === undefined) { - return false; - } - - const removeTimestamp = this._removes.get(element); - if (removeTimestamp === undefined) { - return true; - } - if (addTimestamp > removeTimestamp) { - return true; - } - if (addTimestamp - removeTimestamp === 0 && this._bias === Bias.ADD) { - return true; - } - - return false; - } - - add(element: T): void { - this._adds.set(element, Date.now()); - } - - remove(element: T): void { - this._removes.set(element, Date.now()); - } - - getAdds(): Map { - return this._adds; - } - - getRemoves(): Map { - return this._removes; - } - - compare(peerSet: LWWElementSet): boolean { - return ( - compareSets(this._adds, peerSet._adds) && - compareSets(this._removes, peerSet._removes) - ); - } - - merge(peerSet: LWWElementSet): void { - for (const [element, timestamp] of peerSet._adds.entries()) { - const thisTimestamp = this._adds.get(element); - if (!thisTimestamp || thisTimestamp < timestamp) { - this._adds.set(element, timestamp); - } - } - for (const [element, timestamp] of peerSet._removes.entries()) { - const thisTimestamp = this._removes.get(element); - if (!thisTimestamp || thisTimestamp < timestamp) { - this._removes.set(element, timestamp); - } - } - } -} - -function compareSets(set1: Map, set2: Map): boolean { - return ( - set1.size === set2.size && [...set1.keys()].every((key) => set2.has(key)) - ); -} diff --git a/packages/crdt/src/crdts/LWWRegister/index.ts b/packages/crdt/src/crdts/LWWRegister/index.ts deleted file mode 100644 index 6f1b11f8..00000000 --- a/packages/crdt/src/crdts/LWWRegister/index.ts +++ /dev/null @@ -1,47 +0,0 @@ -export class LWWRegister { - private _element: T; - private _timestamp: number; - private _nodeId: string; - - constructor(element: T, nodeId: string) { - this._element = element; - this._timestamp = Date.now(); - this._nodeId = nodeId; - } - - assign(element: T, nodeId: string): void { - this._element = element; - this._timestamp = Date.now(); - this._nodeId = nodeId; - } - - getElement(): T { - return this._element; - } - - getTimestamp(): number { - return this._timestamp; - } - - getNodeId(): string { - return this._nodeId; - } - - compare(register: LWWRegister): boolean { - return this._timestamp <= register.getTimestamp(); - } - - merge(register: LWWRegister): void { - const otherTimestamp = register.getTimestamp(); - const otherNodeId = register.getNodeId(); - if (otherTimestamp < this._timestamp) { - return; - } - if (otherTimestamp === this._timestamp && otherNodeId <= this._nodeId) { - return; - } - this._element = register.getElement(); - this._timestamp = otherTimestamp; - this._nodeId = otherNodeId; - } -} diff --git a/packages/crdt/src/crdts/OORSet/index.ts b/packages/crdt/src/crdts/OORSet/index.ts deleted file mode 100644 index 9f9002ed..00000000 --- a/packages/crdt/src/crdts/OORSet/index.ts +++ /dev/null @@ -1,90 +0,0 @@ -export interface ElementTuple { - element: T; - tag: number; - nodeId: string; -} - -/* Implementation of the Optimized Observed-Remove Set CRDT - Based on the paper: https://pages.lip6.fr/Marek.Zawirski/papers/RR-8083.pdf -*/ -export class OORSet { - elements: Set> = new Set>(); - summary: Map = new Map(); - nodeId = ""; - - constructor(nodeId?: string, elements?: Set>) { - if (nodeId !== undefined) { - this.nodeId = nodeId; - this.summary = new Map([ - [this.nodeId, this.elements.size], - ]); - } - - if (elements !== undefined) { - this.elements = elements; - } - } - - lookup(element: T): boolean { - return [...this.elements].some((elem) => elem.element === element); - } - - add(nodeId: string, element: T): void { - const tag: number = (this.summary.get(this.nodeId) ?? 0) + 1; - this.summary.set(this.nodeId, tag); - this.elements.add({ element, tag, nodeId: nodeId }); - } - - remove(element: T): void { - for (const tuple of this.elements.values()) { - if (tuple.element === element) { - this.elements.delete(tuple); //removes element from the elements - } - } - } - - // When comparing both element sets, it just needs to compare them one way because - // the "tag" and "nodeId" are going to be unique for a given element so there are - // not equal elements in the set before they're merged - compare(peerSet: OORSet): boolean { - return ( - this.elements.size === peerSet.elements.size && - [...this.elements].every((value) => peerSet.elements.has(value)) - ); - } - - merge(peerSet: OORSet): void { - // place: [local, remote, both] - // sets: [elements, removed] - // existence: [in, notIn] - const bothInElements = [...this.elements].filter((element) => - peerSet.elements.has(element), - ); - const localInElementsRemoteNotInRemoved = [...this.elements].filter( - (element) => - !peerSet.elements.has(element) && - element.tag > (peerSet.summary.get(element.nodeId) ?? 0), - ); - const localNotInRemovedRemoteInElements = [...peerSet.elements].filter( - (element) => - !this.elements.has(element) && - element.tag > (this.summary.get(element.nodeId) ?? 0), - ); - - this.elements = new Set>([ - ...bothInElements, - ...localInElementsRemoteNotInRemoved, - ...localNotInRemovedRemoteInElements, - ]); - this.elements = new Set( - [...this.elements].filter((e) => - [...this.elements].every((e2) => e.tag > e2.tag), - ), - ); - - // update summary - for (const e of peerSet.summary.entries()) { - this.summary.set(e[0], Math.max(e[1], this.summary.get(e[0]) ?? 0)); - } - } -} diff --git a/packages/crdt/src/crdts/PNCounter/index.ts b/packages/crdt/src/crdts/PNCounter/index.ts deleted file mode 100644 index f698aed8..00000000 --- a/packages/crdt/src/crdts/PNCounter/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { GCounter } from "../GCounter/index.js"; - -/// State-based PNCounter -export class PNCounter { - private _increments: GCounter; - private _decrements: GCounter; - - constructor(increments: GCounter, decrements: GCounter) { - this._increments = increments; - this._decrements = decrements; - } - - value(): number { - return this._increments.value() - this._decrements.value(); - } - - increments(): GCounter { - return this._increments; - } - - decrements(): GCounter { - return this._decrements; - } - - increment(nodeId: string, amount: number): void { - this._increments.increment(nodeId, amount); - } - - decrement(nodeId: string, amount: number): void { - this._decrements.increment(nodeId, amount); - } - - compare(peerCounter: PNCounter): boolean { - return ( - this._increments.compare(peerCounter.increments()) && - this._decrements.compare(peerCounter.decrements()) - ); - } - - merge(peerCounter: PNCounter): void { - this._increments.merge(peerCounter.increments()); - this._decrements.merge(peerCounter.decrements()); - } -} diff --git a/packages/crdt/src/crdts/RGA/README.md b/packages/crdt/src/crdts/RGA/README.md deleted file mode 100644 index d746614e..00000000 --- a/packages/crdt/src/crdts/RGA/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# RGA (Replicated Growable Array) CRDT - -This is an implementation of a Replicated Growable Array (RGA) Conflict-free Replicated Data Type (CRDT). RGA is a data structure that allows for concurrent editing of an array by multiple nodes, while automatically resolving most conflicts. - -## Overview - -The structure is designed to maintain a consistent state across multiple replicas, even when they receive operations in different orders. It achieves this by using unique identifiers for each element and a tombstone mechanism for deletions. - -## Key Components - -### RGAElement - -Each element in the RGA is represented by an `RGAElement` object with the following properties: - -- `vid`: A unique identifier for the element, consisting of a counter and a node ID. -- `value`: The actual value stored in the element (or null if the element is deleted). -- `parent`: The identifier of the element that precedes this one in the logical order. -- `isDeleted`: A flag indicating whether the element has been deleted. - -### RGA Class - -The `RGA` class manages the sequencer, which generates new identifiers, and the array of `RGAElement` objects providing methods for manipulating the array. - -## Main Operations - -1. **Insert**: Adds a new element at a specified index. -2. **Delete**: Marks an element as deleted (tombstone) at a specified index. -3. **Update**: Changes the value of an element at a specified index. -4. **Merge**: Combines the current RGA with another RGA, resolving conflicts automatically. -5. **getArray**: Serialises the current state of the RGA to an array of values. - -## How It Works - -1. **Unique Identifiers**: Each element has a unique identifier (`vid`) generated using a sequencer. - -2. **Logical Ordering**: Elements are ordered based on their `parent` references and `vid` comparisons. - -3. **Tombstones**: Deleted elements are not removed but marked as tombstones with the isDeleted property. - -4. **Conflict Resolution**: - - - For concurrent inserts at the same position (same parent element), the element with the higher `vid` is placed first. - - If the parent elements are different, the elements are inserted in the order of their parent's index. - - Deletions are preserved due to the tombstone mechanism. - -5. **Merging**: When merging two RGAs, elements from the peer RGA are inserted into the current RGA, maintaining the correct order and resolving the conflicts. - -## Limitations - -The RGA may not be able to resolve complex backward interleaving scenarios. The insertElement method primarily relies on the parent index and a simple comparison of virtual IDs (vids) to determine the insertion position. This may lead to conflicts in some edge cases. diff --git a/packages/crdt/src/crdts/RGA/index.ts b/packages/crdt/src/crdts/RGA/index.ts deleted file mode 100644 index 1900a57e..00000000 --- a/packages/crdt/src/crdts/RGA/index.ts +++ /dev/null @@ -1,164 +0,0 @@ -/// Replicable Growable Array (RGA) CRDT -type Identifier = { counter: number; nodeId: string }; - -class RGAElement { - // Virtual identifier of the element - vid: Identifier; - value: T | null; - parent: Identifier | null; - isDeleted: boolean; - - constructor( - vid: Identifier, - value: T | null, - parent: Identifier | null, - isDeleted = false, - ) { - this.vid = vid; - this.value = value; - this.parent = parent; - this.isDeleted = isDeleted; - } -} - -export class RGA { - /// The sequencer is used to generate unique identifiers for each element - sequencer: Identifier; - /// For now we are using a simple array to store elements - /// This can be optimized using a Btree - elements: RGAElement[]; - - /* - We are using an empty element as the head of the array to simplify the logic of merging two RGA instances. - It acts as an anchor and is the same for all replicas. - */ - constructor( - nodeId: string, - sequencer: Identifier = { counter: 0, nodeId: nodeId }, - elements: RGAElement[] = [ - new RGAElement({ counter: 0, nodeId: "" }, null, null, true), - ], - ) { - this.sequencer = sequencer; - this.elements = elements; - } - - getArray(): T[] { - return this.elements - .filter((element) => !element.isDeleted) - .map((element) => element.value as T); - } - - clear(): void { - this.sequencer = { counter: 0, nodeId: this.sequencer.nodeId }; - this.elements = [ - new RGAElement({ counter: 0, nodeId: "" }, null, null, true), - ]; - } - - // Function to generate the next unique identifier - private nextSeq(sequencer: Identifier): Identifier { - return { counter: sequencer.counter + 1, nodeId: sequencer.nodeId }; - } - - // Check whether a < b, ids are never equal - private compareVIds(a: Identifier, b: Identifier): boolean { - if (a.counter !== b.counter) { - return a.counter < b.counter; - } - return a.nodeId < b.nodeId; - } - - // Function to map a logical index (ignoring tombstones) to a physical index in the elements array - private indexWithTombstones(index: number): number { - let offset = 1; // Start from 1 to skip the head element - let i = index; - - while (i > 0) { - if (!this.elements[offset].isDeleted) i--; - offset++; - } - return offset; - } - - // Function to read the value at a given index - read(index: number): T | null { - let i = this.indexWithTombstones(index); - while (this.elements[i].isDeleted) i++; - return this.elements[i].value; - } - - // Function to find the physical index of an element given the virtual id - private indexOfVId(ptr: Identifier | null): number { - if (!ptr) return -1; - for (let offset = 0; offset < this.elements.length; offset++) { - if ( - ptr.counter === this.elements[offset].vid.counter && - ptr.nodeId === this.elements[offset].vid.nodeId - ) { - return offset; - } - } - return -1; - } - - // Function to insert a new element after a given index, might not be immidiately after becuase we look at parents - insert(parentIndex: number, value: T): void { - const i = this.indexWithTombstones(parentIndex); - const parent = this.elements[i - 1].vid; - const newVId = this.nextSeq(this.sequencer); - this.insertElement(new RGAElement(newVId, value, parent)); - } - - // Function to insert a new element into the array - private insertElement(element: RGAElement): void { - const parentIdx = this.indexOfVId(element.parent); - let insertIdx = parentIdx + 1; - for (; insertIdx < this.elements.length; insertIdx++) { - const curr = this.elements[insertIdx]; - const currParentIdx = this.indexOfVId(curr.parent); - if (currParentIdx > parentIdx) break; - if (currParentIdx === parentIdx) { - if (this.compareVIds(curr.vid, element.vid)) break; - } - } - this.sequencer = { - ...this.sequencer, - counter: Math.max(this.sequencer.counter, element.vid.counter), - }; - // Check if its a duplicate - if ( - this.elements[insertIdx - 1].vid.counter === element.vid.counter && - this.elements[insertIdx - 1].vid.nodeId === element.vid.nodeId - ) { - return; - } - this.elements.splice(insertIdx, 0, element); - } - - // Function to delete an element from the RGA - delete(index: number): void { - let i = this.indexWithTombstones(index); - while (this.elements[i].isDeleted) i++; - this.elements[i].isDeleted = true; - } - - // Function to update the value of an element - update(index: number, value: T): void { - let i = this.indexWithTombstones(index); - while (this.elements[i].isDeleted) i++; - this.elements[i].value = value; - } - - // Merge another RGA instance into this one - merge(peerRGA: RGA): void { - for (let i = 1; i < peerRGA.elements.length; i++) { - this.insertElement(peerRGA.elements[i]); - } - - this.sequencer = { - ...this.sequencer, - counter: Math.max(this.sequencer.counter, peerRGA.sequencer.counter), - }; - } -} diff --git a/packages/crdt/src/index.asc.ts b/packages/crdt/src/index.asc.ts deleted file mode 100644 index 7706029c..00000000 --- a/packages/crdt/src/index.asc.ts +++ /dev/null @@ -1,7 +0,0 @@ -// export * from "./builtins/2PSet/index.js"; -// export * from "./builtins/GCounter/index.js"; -export * from "./crdts/GSet/index.js"; -// export * from "./builtins/IPSet/index.js"; -// export * from "./builtins/LWWElementSet/index.js"; -// export * from "./builtins/LWWRegister/index.js"; -// export * from "./builtins/PNCounter/index.js"; diff --git a/packages/crdt/src/index.ts b/packages/crdt/src/index.ts deleted file mode 100644 index fe7ff16d..00000000 --- a/packages/crdt/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from "./crdts/2PSet/index.js"; -export * from "./crdts/GCounter/index.js"; -export * from "./crdts/GSet/index.js"; -export * from "./crdts/IPSet/index.js"; -export * from "./crdts/LWWElementSet/index.js"; -export * from "./crdts/LWWRegister/index.js"; -export * from "./crdts/OORSet/index.js"; -export * from "./crdts/PNCounter/index.js"; -export * from "./crdts/RGA/index.js"; - -export * from "./cros/AddWinsSet/index.js"; diff --git a/packages/crdt/tests/GCounter.test.ts b/packages/crdt/tests/GCounter.test.ts deleted file mode 100644 index e77a555e..00000000 --- a/packages/crdt/tests/GCounter.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { GCounter } from "../src/crdts/GCounter/index.js"; - -describe("G-Counter Tests", () => { - let set1: GCounter; - - beforeEach(() => { - set1 = new GCounter({ node1: 5, node2: 10 }); - }); - - test("Test Initial Values", () => { - expect(set1.value()).toBe(15); - }); - - test("Test Increment", () => { - set1.increment("node1", 10); - set1.increment("node2", 5); - - expect(set1.value()).toBe(30); - }); - - test("Test Compare", () => { - const set2 = new GCounter({ node1: 5, node2: 10 }); - const set3 = new GCounter({ node1: 5, node2: 10, node3: 15 }); - - expect(set1.compare(set2)).toBe(true); - set1.increment("node1", 5); - expect(set1.compare(set2)).toBe(false); - expect(set1.compare(set3)).toBe(false); - }); - - test("Test Merge", () => { - const set2 = new GCounter({ node1: 3, node2: 10 }); - const set3 = new GCounter({ node1: 5, node3: 15 }); - - expect(set1.counts).toEqual({ node1: 5, node2: 10 }); - set2.merge(set1); - expect(set2.counts).toEqual({ node1: 5, node2: 10 }); - set1.merge(set3); - expect(set1.counts).toEqual({ node1: 5, node2: 10, node3: 15 }); - }); -}); diff --git a/packages/crdt/tests/GSet.test.ts b/packages/crdt/tests/GSet.test.ts deleted file mode 100644 index 3b95930f..00000000 --- a/packages/crdt/tests/GSet.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { afterEach, beforeEach, describe, expect, test } from "vitest"; -import { GSet } from "../src/crdts/GSet/index.js"; - -describe("G-Set Tests", () => { - let set1: GSet; - let set2: GSet; - - beforeEach(() => { - set1 = new GSet(new Set(["walter", "jesse", "mike"])); - set2 = new GSet(new Set(["walter", "jesse", "mike"])); - }); - - test("Test Add", () => { - set1.add("gustavo"); - set2.add("gustavo"); - - expect(set1.lookup("gustavo")).toBe(true); - expect(set2.lookup("gustavo")).toBe(true); - }); - - test("Test Compare", () => { - expect(set1.compare(set2)).toBe(true); - - set1.add("gustavo"); - - expect(set1.compare(set2)).toBe(false); - - set2.add("gustavo"); - - expect(set1.compare(set2)).toBe(true); - }); - - test("Test Merge", () => { - set1.add("gustavo"); - set2.add("lalo"); - - expect(set1.compare(set2)).toBe(false); - - set1.merge(set2); - set2.merge(set1); - - expect(set1.compare(set2)).toBe(true); - }); -}); diff --git a/packages/crdt/tests/IPSet.test.ts b/packages/crdt/tests/IPSet.test.ts deleted file mode 100644 index 774bfdb5..00000000 --- a/packages/crdt/tests/IPSet.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { GCounter } from "../src/crdts/GCounter/index.js"; -import { IPSet } from "../src/crdts/IPSet/index.js"; - -describe("Infinite-phase set Tests", () => { - const nodeId = "node"; - - let set1: IPSet; - let set2: IPSet; - let set3: IPSet; - - beforeEach(() => { - set1 = new IPSet(); - set2 = new IPSet(); - set3 = new IPSet(); - }); - - test("Test Add Elements", () => { - expect(set1.contains("alice")).toBe(false); - - set1.add(nodeId, "alice"); - expect(set1.contains("alice")).toBe(true); - expect(set1.contains("bob")).toBe(false); - - set1.add(nodeId, "alice"); - expect(set1.contains("alice")).toBe(true); - }); - - test("Test Add and Remove Elements", () => { - expect(set1.contains("alice")).toBe(false); - - set1.add(nodeId, "alice"); - set1.remove(nodeId, "alice"); - expect(set1.contains("alice")).toBe(false); - - set1.add(nodeId, "alice"); - set1.add(nodeId, "alice"); - set1.remove(nodeId, "alice"); - expect(set1.contains("alice")).toBe(false); - - set1.add(nodeId, "alice"); - set1.add(nodeId, "alice"); - set1.remove(nodeId, "alice"); - set1.remove(nodeId, "alice"); - set1.add(nodeId, "alice"); - set1.add(nodeId, "alice"); - set1.add(nodeId, "alice"); - expect(set1.contains("alice")).toBe(true); - - set1.add(nodeId, "alice"); - set1.remove(nodeId, "alice"); - set1.add(nodeId, "alice"); - set1.remove(nodeId, "alice"); - set1.add(nodeId, "alice"); - expect(set1.contains("alice")).toBe(true); - }); - - describe("Test Merge Elements", () => { - test("Merge Sets Overlapping", () => { - set2.add(nodeId, 1); - set2.add(nodeId, 1); - set2.remove(nodeId, 3); - set2.add(nodeId, 3); - set2.remove(nodeId, 3); - set2.add(nodeId, 5); - set2.remove(nodeId, 5); - set2.add(nodeId, 5); - - set3.add(nodeId, 1); - set3.remove(nodeId, 1); - set3.add(nodeId, 3); - - set2.merge(set3); - expect(set2).toStrictEqual( - new IPSet( - new Map([ - [1, new GCounter({ [nodeId]: 2 })], - [3, new GCounter({ [nodeId]: 2 })], - [5, new GCounter({ [nodeId]: 3 })], - ]), - ), - ); - }); - test("Merge Sets Non-Overlapping", () => { - set2.add(nodeId, 1); - set2.add(nodeId, 3); - - set3.add(nodeId, 5); - set3.add(nodeId, 7); - - set3.merge(set2); - expect(set3).toStrictEqual( - new IPSet( - new Map([ - [1, new GCounter({ [nodeId]: 1 })], - [3, new GCounter({ [nodeId]: 1 })], - [5, new GCounter({ [nodeId]: 1 })], - [7, new GCounter({ [nodeId]: 1 })], - ]), - ), - ); - }); - }); - - test("Test Compare Sets", () => { - expect(set2.compare(set2)).toBe(true); - expect(set2.compare(set3)).toBe(true); - expect(set3.compare(set2)).toBe(true); - - set2.add(nodeId, 1); - - expect(set2.compare(set3)).toBe(false); - expect(set3.compare(set2)).toBe(true); - - set3.add(nodeId, 1); - expect(set2.compare(set3)).toBe(true); - expect(set3.compare(set2)).toBe(true); - - set2.remove(nodeId, 1); - expect(set2.compare(set3)).toBe(false); - expect(set3.compare(set2)).toBe(true); - }); - - test("Test set() function", () => { - for (const i of [1, 2, 3, 4, 5]) { - set2.add(nodeId, i); - } - expect(set2.set()).toStrictEqual(new Set([1, 2, 3, 4, 5])); - - for (const i of [1, 3, 4]) { - set2.remove(nodeId, i); - } - expect(set2.set()).toStrictEqual(new Set([2, 5])); - - for (const i of [1, 2, 3]) { - set2.remove(nodeId, i); - } - expect(set2.set()).toStrictEqual(new Set([5])); - - for (const i of [1, 2, 3]) { - set2.add(nodeId, i); - } - expect(set2.set()).toStrictEqual(new Set([1, 2, 3, 5])); - }); -}); diff --git a/packages/crdt/tests/LWWElementSet.test.ts b/packages/crdt/tests/LWWElementSet.test.ts deleted file mode 100644 index 0e96be74..00000000 --- a/packages/crdt/tests/LWWElementSet.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { Bias, LWWElementSet } from "../src/crdts/LWWElementSet/index.js"; - -describe("LWW-Element-Set Tests", () => { - const testValues = ["walter", "jesse", "mike"]; - - let set1: LWWElementSet; - let set2: LWWElementSet; - let set3: LWWElementSet; - - beforeEach(() => { - set1 = new LWWElementSet(new Map(), new Map(), Bias.ADD); - set2 = new LWWElementSet(new Map(), new Map(), Bias.ADD); - set3 = new LWWElementSet(new Map(), new Map(), Bias.REMOVE); - - for (const value of testValues) { - set1.add(value); - set2.add(value); - set3.add(value); - } - }); - - test("Test Add Elements", () => { - expect(set1.lookup("gustavo")).toBe(false); - - set1.add("gustavo"); - expect(set1.lookup("gustavo")).toBe(true); - }); - - test("Test Remove Elements", () => { - expect(set1.lookup("mike")).toBe(true); - - set1.getRemoves().set("mike", Date.now() + 1); - - expect(set1.lookup("mike")).toBe(false); - }); - - test("Test Compare Sets", () => { - expect(set1.compare(set2)).toBe(true); - expect(set1.compare(set3)).toBe(true); - expect(set3.compare(set2)).toBe(true); - - set1.remove("jesse"); - - expect(set1.compare(set2)).toBe(false); - expect(set1.compare(set3)).toBe(false); - expect(set3.compare(set2)).toBe(true); - }); - - describe("Test Merge Elements", () => { - test("Merge Sets", () => { - // Adding different names to each set - set1.add("gustavo"); - set2.add("saul"); - - expect(set1.compare(set2)).toBe(false); - - set1.merge(set2); - set2.merge(set1); - - expect(set1.compare(set2)).toBe(true); - }); - - test("Same Element, different Timestamps", () => { - const timestamp = Date.now(); - set1.getAdds().set("gustavo", timestamp); - set2.getAdds().set("gustavo", timestamp + 5); - - expect(set1.getAdds().get("gustavo")).toBe(timestamp); - - set1.merge(set2); - set2.merge(set1); - - expect(set1.getAdds().get("gustavo")).toBe(timestamp + 5); - expect(set2.getAdds().get("gustavo")).toBe(timestamp + 5); - }); - - test("Merge Removal Timestamps", () => { - const timestamp = Date.now(); - - set1.getAdds().set("gustavo", timestamp); - set2.getRemoves().set("gustavo", timestamp + 5); - - set1.merge(set2); - - expect(set1.lookup("gustavo")).toBe(false); - expect(set1.getRemoves().get("gustavo")).toBe(timestamp + 5); - }); - }); -}); diff --git a/packages/crdt/tests/LWWRegister.test.ts b/packages/crdt/tests/LWWRegister.test.ts deleted file mode 100644 index cd08a1d8..00000000 --- a/packages/crdt/tests/LWWRegister.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { beforeEach, describe, expect, test, vi } from "vitest"; -import { LWWRegister } from "../src/crdts/LWWRegister/index.js"; - -describe("LWW-Register Tests", () => { - test("Test Assign", () => { - const register1 = new LWWRegister("alice", "node1"); - - expect(register1.getElement()).toBe("alice"); - expect(register1.getNodeId()).toEqual("node1"); - register1.assign("bob", "node2"); - expect(register1.getElement()).toBe("bob"); - expect(register1.getNodeId()).toEqual("node2"); - }); - - test("Test Compare", () => { - vi.useFakeTimers(); - const date = new Date(2000, 1, 1, 13); - vi.setSystemTime(date); - - const register1 = new LWWRegister("alice", "node1"); - - vi.useRealTimers(); - - const register2 = new LWWRegister("alice", "node2"); - - expect(register1.compare(register2)).toEqual(true); - expect(register2.compare(register1)).toEqual(false); - }); - - test("Test Merge", () => { - const register1 = new LWWRegister("alice", "node1"); - const register2 = new LWWRegister("bob", "node2"); - - register1.merge(register2); - expect(register1.getElement()).toEqual("bob"); - expect(register2.getNodeId()).toEqual("node2"); - - register2.merge(register1); - expect(register1.getElement()).toEqual("bob"); - expect(register2.getNodeId()).toEqual("node2"); - }); - - test("Test Merge w/same timestamp", () => { - vi.useFakeTimers(); - const date = new Date(2000, 1, 1, 13); - vi.setSystemTime(date); - - const register1 = new LWWRegister("alice", "node1"); - const register2 = new LWWRegister("bob", "node2"); - - expect(register1.getElement()).toBe("alice"); - expect(register1.getNodeId()).toEqual("node1"); - - register1.merge(register2); - expect(register1.getElement()).toBe("bob"); - expect(register1.getNodeId()).toEqual("node2"); - - register2.merge(register1); - expect(register1.getElement()).toBe("bob"); - expect(register1.getNodeId()).toEqual("node2"); - - vi.useRealTimers(); - }); -}); diff --git a/packages/crdt/tests/OORSet.test.ts b/packages/crdt/tests/OORSet.test.ts deleted file mode 100644 index 85cb658b..00000000 --- a/packages/crdt/tests/OORSet.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { type ElementTuple, OORSet } from "../src/crdts/OORSet"; - -describe("OR-Set Tests", () => { - let set1: OORSet; - let set2: OORSet; - - const testValues = ["walter", "jesse", "mike"]; - - beforeEach(() => { - set1 = new OORSet("set1", new Set>()); - set2 = new OORSet("set2", new Set>()); - - for (const value of testValues) { - set1.add("set1", value); - set2.add("set2", value); - } - }); - - test("Test Add Elements", () => { - expect(set1.lookup("gustavo")).toBe(false); - - set1.add("set1", "gustavo"); - - expect(set1.lookup("gustavo")).toBe(true); - }); - - test("Test Remove Elements", () => { - expect(set1.lookup("mike")).toBe(true); - - set1.remove("mike"); - - expect(set1.lookup("mike")).toBe(false); - }); - - test("Test Merge Elements", () => { - expect(set1.compare(set2)).toBe(false); - - set1.merge(set2); - set2.merge(set1); - - expect(set1.compare(set2)).toBe(true); - }); -}); diff --git a/packages/crdt/tests/PNCounter.test.ts b/packages/crdt/tests/PNCounter.test.ts deleted file mode 100644 index e069f4bd..00000000 --- a/packages/crdt/tests/PNCounter.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { GCounter } from "../src/crdts/GCounter/index.js"; -import { PNCounter } from "../src/crdts/PNCounter/index.js"; - -describe("PN-Counter Tests", () => { - let set1: PNCounter; - let set2: PNCounter; - - beforeEach(() => { - set1 = new PNCounter( - new GCounter({ node1: 5, node2: 10, node3: 15 }), - new GCounter({ node1: 3, node2: 4, node3: 3 }), - ); - set2 = new PNCounter( - new GCounter({ node1: 5, node2: 10, node3: 15 }), - new GCounter({ node1: 3, node2: 4, node3: 3 }), - ); - }); - - test("Test Initial Value", () => { - expect(set1.value()).toBe(20); - expect(set2.value()).toBe(20); - }); - - test("Test Increment", () => { - set1.increment("node1", 10); - set2.increment("node1", 20); - expect(set1.value()).toBe(30); - expect(set2.value()).toBe(40); - }); - - test("Test Decrement", () => { - set1.decrement("node1", 10); - set2.decrement("node1", 20); - expect(set1.value()).toBe(10); - expect(set2.value()).toBe(0); - }); - - test("Test Compare", () => { - expect(set1.compare(set2)).toBe(true); - set1.decrement("node1", 10); - expect(set1.compare(set2)).toBe(false); - set2.decrement("node1", 10); - expect(set1.compare(set2)).toBe(true); - }); - - test("Test Merge", () => { - set1.increment("node1", 10); - set2.decrement("node2", 5); - expect(set1.compare(set2)).toBe(false); - expect(set2.compare(set1)).toBe(false); - set1.merge(set2); - set2.merge(set1); - expect(set1.compare(set2)).toBe(true); - expect(set2.compare(set1)).toBe(true); - }); -}); diff --git a/packages/crdt/tests/RGA.test.ts b/packages/crdt/tests/RGA.test.ts deleted file mode 100644 index ed894e0a..00000000 --- a/packages/crdt/tests/RGA.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { RGA } from "../src/crdts/RGA/index.js"; - -describe("Replicable Growable Array Tests", () => { - let rga: RGA; - let peerRGA: RGA; - - beforeEach(() => { - rga = new RGA("node1"); - peerRGA = new RGA("node2"); - }); - - test("Test Insert", () => { - rga.insert(0, "A"); - rga.insert(1, "B"); - rga.insert(1, "C"); - rga.insert(0, "D"); - - expect(rga.getArray()).toEqual(["D", "A", "C", "B"]); - }); - - test("Test Read", () => { - rga.insert(0, "A"); - rga.insert(1, "B"); - rga.insert(1, "C"); - rga.delete(1); - - expect(rga.read(0)).toBe("A"); - expect(rga.read(1)).toBe("B"); - }); - - test("Test Insert and Delete", () => { - rga.insert(0, "A"); - rga.insert(1, "B"); - rga.insert(1, "C"); - rga.delete(0); - rga.delete(0); - expect(rga.getArray()).toEqual(["B"]); - - rga.clear(); - - rga.insert(0, "A"); - rga.insert(1, "B"); - rga.delete(0); - - expect(rga.getArray()).toEqual(["B"]); - - rga.insert(0, "C"); - rga.insert(1, "D"); - expect(rga.getArray()).toEqual(["C", "D", "B"]); - - rga.delete(1); - expect(rga.getArray()).toEqual(["C", "B"]); - - rga.delete(1); - expect(rga.getArray()).toEqual(["C"]); - - peerRGA.insert(0, "E"); - peerRGA.insert(0, "F"); - peerRGA.insert(2, "G"); - peerRGA.insert(3, "H"); - peerRGA.delete(1); - peerRGA.delete(1); - peerRGA.delete(1); - expect(peerRGA.getArray()).toEqual(["F"]); - }); - - test("Test Update", () => { - rga.insert(0, "A"); - rga.insert(1, "B"); - rga.update(0, "C"); - rga.update(1, "D"); - - expect(rga.getArray()).toEqual(["C", "D"]); - }); - - test("Test Merge", () => { - rga.insert(0, "A"); - rga.insert(1, "B"); - - peerRGA.insert(0, "C"); - peerRGA.insert(1, "D"); - peerRGA.insert(0, "E"); - - rga.merge(peerRGA); - expect(rga.getArray()).toEqual(["E", "C", "A", "D", "B"]); - - peerRGA.merge(rga); - expect(peerRGA.getArray()).toEqual(rga.getArray()); - }); -}); diff --git a/packages/crdt/tests/TwoPSet.test.ts b/packages/crdt/tests/TwoPSet.test.ts deleted file mode 100644 index dfc80e57..00000000 --- a/packages/crdt/tests/TwoPSet.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { beforeEach, describe, expect, test } from "vitest"; -import { TwoPSet } from "../src/crdts/2PSet/index.js"; -import { GSet } from "../src/crdts/GSet/index.js"; - -describe("2P-Set Tests", () => { - let set1: TwoPSet; - let set2: TwoPSet; - - beforeEach(() => { - set1 = new TwoPSet( - new GSet(new Set(["walter", "jesse", "mike"])), - new GSet(new Set()), - ); - set2 = new TwoPSet( - new GSet(new Set(["walter", "jesse", "mike"])), - new GSet(new Set()), - ); - }); - - test("Test Add Element", () => { - expect(set1.lookup("gustavo")).toBe(false); - - set1.add("gustavo"); - - expect(set1.lookup("gustavo")).toBe(true); - }); - - test("Test Remove Element", () => { - expect(set1.lookup("mike")).toBe(true); - - set1.remove("mike"); - - expect(set1.lookup("mike")).toBe(false); - }); - - test("Test Compare Elements", () => { - expect(set1.compare(set2)).toBe(true); - - set1.remove("mike"); - - expect(set1.compare(set2)).toBe(false); - - set2.remove("mike"); - - expect(set1.compare(set2)).toBe(true); - }); - - test("Test Merge Elements", () => { - set1.remove("mike"); - set2.add("gustavo"); - - expect(set1.compare(set2)).toBe(false); - - set1.merge(set2); - set2.merge(set1); - - expect(set1.compare(set2)).toBe(true); - }); -}); diff --git a/packages/node/package.json b/packages/node/package.json index 6cf1848c..c7cc2994 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -1,46 +1,41 @@ { - "name": "@topology-foundation/node", - "version": "0.1.1", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/topology-foundation/ts-topology.git" - }, - "type": "module", - "types": "./dist/src/index.d.ts", - "files": [ - "src", - "dist", - "!dist/test", - "!**/*.tsbuildinfo" - ], - "exports": { - ".": { - "types": "./dist/src/index.d.ts", - "import": "./dist/src/index.js" - } - }, - "scripts": { - "build": "tsc -b", - "clean": "rm -rf dist/ node_modules/", - "cli": "tsx ./src/run.ts", - "prebuild": "node -p \"'export const VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts", - "prepack": "tsc -b", - "test": "vitest" - }, - "devDependencies": { - "@types/node": "^22.5.4", - "tsx": "4.19.0", - "typescript": "^5.5.4", - "vitest": "^2.0.5" - }, - "dependencies": { - "@chainsafe/libp2p-gossipsub": "^13.1.0", - "@libp2p/interface": "^1.7.0", - "@topology-foundation/crdt": "0.1.1", - "@topology-foundation/network": "0.1.1", - "@topology-foundation/object": "0.1.1", - "commander": "^12.1.0", - "it-length-prefixed": "^9.1.0" - } + "name": "@topology-foundation/node", + "version": "0.1.1", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/topology-foundation/ts-topology.git" + }, + "type": "module", + "types": "./dist/src/index.d.ts", + "files": ["src", "dist", "!dist/test", "!**/*.tsbuildinfo"], + "exports": { + ".": { + "types": "./dist/src/index.d.ts", + "import": "./dist/src/index.js" + } + }, + "scripts": { + "build": "tsc -b", + "clean": "rm -rf dist/ node_modules/", + "cli": "tsx ./src/run.ts", + "prebuild": "node -p \"'export const VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts", + "prepack": "tsc -b", + "test": "vitest" + }, + "devDependencies": { + "@types/node": "^22.5.4", + "tsx": "4.19.0", + "typescript": "^5.5.4", + "vitest": "^2.0.5" + }, + "dependencies": { + "@chainsafe/libp2p-gossipsub": "^13.1.0", + "@libp2p/interface": "^1.7.0", + "@topology-foundation/blueprints": "0.1.1", + "@topology-foundation/network": "0.1.1", + "@topology-foundation/object": "0.1.1", + "commander": "^12.1.0", + "it-length-prefixed": "^9.1.0" + } } diff --git a/packages/node/tsconfig.json b/packages/node/tsconfig.json index af1c9e05..1afdb409 100644 --- a/packages/node/tsconfig.json +++ b/packages/node/tsconfig.json @@ -5,7 +5,7 @@ }, "references": [ { - "path": "../crdt" + "path": "../blueprints" }, { "path": "../network" diff --git a/packages/object/asconfig.json b/packages/object/asconfig.json index e349ea74..ccd4289b 100644 --- a/packages/object/asconfig.json +++ b/packages/object/asconfig.json @@ -17,7 +17,7 @@ }, "options": { "lib": { - "@topology-foundation/crdt": ["../crdt/src/index.asc.ts"] + "@topology-foundation/blueprints": ["../blueprints/src/index.asc.ts"] } } } diff --git a/packages/object/src/wasm/compiler.ts b/packages/object/src/wasm/compiler.ts index fd02b3b3..d8482a2b 100644 --- a/packages/object/src/wasm/compiler.ts +++ b/packages/object/src/wasm/compiler.ts @@ -15,8 +15,8 @@ export async function compileWasm(path: string) { return fs .readFileSync(filename, "utf8") .replace( - "@topology-foundation/crdt", - "@topology-foundation/crdt/src/index.asc", + "@topology-foundation/blueprints", + "@topology-foundation/blueprints/src/index.asc", ); }, writeFile: ( diff --git a/packages/object/tests/hashgraph.test.ts b/packages/object/tests/hashgraph.test.ts index c281fe02..416497bf 100644 --- a/packages/object/tests/hashgraph.test.ts +++ b/packages/object/tests/hashgraph.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, test } from "vitest"; -import { AddWinsSet } from "../../crdt/src/cros/AddWinsSet/index.js"; -import { PseudoRandomWinsSet } from "../../crdt/src/cros/PseudoRandomWinsSet/index.js"; +import { AddWinsSet } from "../../blueprints/src/AddWinsSet/index.js"; +import { PseudoRandomWinsSet } from "../../blueprints/src/PseudoRandomWinsSet/index.js"; import { TopologyObject } from "../src/index.js"; describe("HashGraph for AddWinSet tests", () => { @@ -319,7 +319,7 @@ describe("HashGraph for PseudoRandomWinsSet tests", () => { test("Test: Many concurrent operations", () => { /* - --- V1:ADD(1) + --- V1:ADD(1) /---- V2:ADD(2) V0:Nop -- V3:ADD(3) \---- V4:ADD(4) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d15d3f20..cae7283a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,16 +45,16 @@ importers: examples/canvas: dependencies: '@topology-foundation/crdt': - specifier: 0.1.1-3 - version: link:../../packages/crdt + specifier: 0.1.1 + version: 0.1.1 '@topology-foundation/network': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../../packages/network '@topology-foundation/node': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../../packages/node '@topology-foundation/object': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../../packages/object crypto-browserify: specifier: ^3.12.0 @@ -86,21 +86,21 @@ importers: version: 5.4.3(@types/node@22.5.4)(terser@5.31.6) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.21.0)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) + version: 0.22.0(rollup@4.21.1)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) examples/chat: dependencies: '@topology-foundation/crdt': - specifier: 0.1.1-3 - version: link:../../packages/crdt + specifier: 0.1.1 + version: 0.1.1 '@topology-foundation/network': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../../packages/network '@topology-foundation/node': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../../packages/node '@topology-foundation/object': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../../packages/object assemblyscript: specifier: ^0.27.29 @@ -141,16 +141,16 @@ importers: version: 5.4.3(@types/node@22.5.4)(terser@5.31.6) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.21.0)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) + version: 0.22.0(rollup@4.21.1)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)) - packages/crdt: + packages/blueprints: dependencies: '@thi.ng/random': specifier: ^4.0.3 version: 4.0.3 devDependencies: '@topology-foundation/object': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../object assemblyscript: specifier: ^0.27.29 @@ -205,7 +205,7 @@ importers: version: 10.0.2 '@libp2p/webrtc': specifier: ^4.1.8 - version: 4.1.8(react-native@0.75.1(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(react@18.3.1)(typescript@5.5.4)) + version: 4.1.8(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(react@18.3.1)(typescript@5.5.4)) '@libp2p/websockets': specifier: ^8.1.2 version: 8.2.0 @@ -249,14 +249,14 @@ importers: '@libp2p/interface': specifier: ^1.7.0 version: 1.7.0 - '@topology-foundation/crdt': - specifier: 0.1.1-3 - version: link:../crdt + '@topology-foundation/blueprints': + specifier: 0.1.1 + version: link:../blueprints '@topology-foundation/network': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../network '@topology-foundation/object': - specifier: 0.1.1-3 + specifier: 0.1.1 version: link:../object commander: specifier: ^12.1.0 @@ -1910,6 +1910,9 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@topology-foundation/crdt@0.1.1': + resolution: {integrity: sha512-wWEK5gkzux73rin5oycOxTsxQhxZuX29H2gZivRs9Sg8zLnEs/7fZFSf/iRGMZy5Qp/dV7qRgbrxcOaMBw9B7Q==} + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -6879,7 +6882,7 @@ snapshots: uint8arraylist: 2.4.8 uint8arrays: 5.1.0 - '@libp2p/webrtc@4.1.8(react-native@0.75.1(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(react@18.3.1)(typescript@5.5.4))': + '@libp2p/webrtc@4.1.8(react-native@0.75.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(react@18.3.1)(typescript@5.5.4))': dependencies: '@chainsafe/libp2p-noise': 15.1.2 '@libp2p/interface': 1.7.0 @@ -7511,6 +7514,8 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} + '@topology-foundation/crdt@0.1.1': {} + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -7521,17 +7526,7 @@ snapshots: '@types/dns-packet@5.6.5': dependencies: - '@types/node': 22.5.0 - - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 9.6.0 - '@types/estree': 1.0.5 - - '@types/eslint@9.6.0': - dependencies: - '@types/estree': 1.0.5 - '@types/json-schema': 7.0.15 + '@types/node': 22.5.4 '@types/estree@1.0.5': {} @@ -11129,7 +11124,7 @@ snapshots: - supports-color - terser - vite-plugin-node-polyfills@0.22.0(rollup@4.21.0)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)): + vite-plugin-node-polyfills@0.22.0(rollup@4.21.1)(vite@5.4.3(@types/node@22.5.4)(terser@5.31.6)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.21.1) node-stdlib-browser: 1.2.0 @@ -11152,7 +11147,7 @@ snapshots: dependencies: esbuild: 0.21.5 postcss: 8.4.45 - rollup: 4.21.0 + rollup: 4.21.1 optionalDependencies: '@types/node': 22.5.4 fsevents: 2.3.3