Skip to content

Commit

Permalink
feat: init
Browse files Browse the repository at this point in the history
  • Loading branch information
zxch3n committed Nov 11, 2023
0 parents commit c3bdd53
Show file tree
Hide file tree
Showing 8 changed files with 553 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/deno_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Deno tests

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

env:
CARGO_TERM_COLOR: always

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Run deno tests
run: deno task test
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true
}
75 changes: 75 additions & 0 deletions 01_basic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Loro, LoroList, LoroMap } from "npm:[email protected]"
import { expect } from "npm:[email protected]"

Deno.test("Basic usage", () => {
/**
* LoroDoc is the entry point for using Loro.
* You must create a Doc to use Map, List, Text, and other CRDT types.
*/
const doc = new Loro();
const list: LoroList = doc.getList("list");
list.insert(0, "A");
list.insert(1, "B");
list.insert(2, "C");

const map: LoroMap = doc.getMap("map");
// map can only has string key
map.set("key", "value");
expect(doc.toJson()).toStrictEqual({
list: ["A", "B", "C"],
map: { key: "value" }
});

// delete 2 element at index 0
list.delete(0, 2)
expect(doc.toJson()).toStrictEqual({
list: ["C"],
map: { key: "value" }
});

});

Deno.test("Sub containers", () => {
/**
* You can create sub CRDT containers in List and Map.
*/
const doc = new Loro();
const list: LoroList = doc.getList("list");
const map: LoroMap = doc.getMap("list");
// insert a List container at index 0, and get the handler to that list
const subList = list.insertContainer(0, "List");
subList.insert(0, "A");
expect(list.getDeepValue()).toStrictEqual([["A"]]);
// create a Text container inside the Map container
const subtext = map.setContainer("text", "Text");
subtext.insert(0, "Hi");
expect(map.getDeepValue()).toStrictEqual({ text: "Hi" });
});

Deno.test("Sync", () => {
/**
* Two documents can complete synchronization with two rounds of exchanges.
*/
const docA = new Loro();
const docB = new Loro();
const listA: LoroList = docA.getList("list");
listA.insert(0, "A");
listA.insert(1, "B");
listA.insert(2, "C");
// B import the ops from A
docB.import(docA.exportFrom());
expect(docB.toJson()).toStrictEqual({
list: ["A", "B", "C"]
})

const listB: LoroList = docB.getList("list");
// delete 1 element at index 1
listB.delete(1, 1);
// A import the missing ops from B
docA.import(docB.exportFrom(docA.version()))
// list at A is now ["A", "C"], with the same state as B
expect(docA.toJson()).toStrictEqual({
list: ["A", "C"]
});
expect(docA.toJson()).toStrictEqual(docB.toJson());
})
97 changes: 97 additions & 0 deletions 02_text.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Delta, Loro } from "npm:[email protected]";
import { expect } from "npm:[email protected]";

Deno.test("Text", () => {
/**
* Loro supports text manipulation.
*/
const doc = new Loro();
const text = doc.getText("text");
text.insert(0, "Hello world!");
text.delete(0, 6);
expect(text.toString()).toBe("world!");
});

Deno.test("Rich text", () => {
/**
* Loro supports rich text CRDTs
*/
const doc = new Loro();
const text = doc.getText("text");
text.insert(0, "Hello world!");
text.mark({ start: 0, end: 5 }, "bold", true);
expect(text.toDelta()).toStrictEqual([{
insert: "Hello",
attributes: { bold: true },
}, {
insert: " world!",
}] as Delta<string>[]);
});

Deno.test("Rich text custom expand behavior - Bold", () => {
/**
* For different styles on rich text, you may want different expand behavior
* when users inserting new text at the boundary of the style.
*
* - Bold: expand the style to cover the new text inserted after the boundary.
* - Link: will not expand the style when inserting new text at the boundary.
*/
const doc = new Loro();
const text = doc.getText("text");
text.insert(0, "Hello world!");
text.mark({ start: 0, end: 5, expand: "after" }, "bold", true);
text.insert(5, "!");
expect(text.toDelta()).toStrictEqual([{
insert: "Hello!",
attributes: { bold: true },
}, {
insert: " world!",
}] as Delta<string>[]);
})


Deno.test("Rich text custom expand behavior - Link", () => {
/**
* For different styles on rich text, you may want different expand behavior
* when users inserting new text at the boundary of the style.
*
* - Bold: expand the style to cover the new text inserted after the boundary.
* - Link: will not expand the style when inserting new text at the boundary.
*/
const doc = new Loro();
const text = doc.getText("text");
text.insert(0, "Hello world!");
text.mark({ start: 0, end: 5, expand: "none" }, "link", true);
text.insert(5, "!");
expect(text.toDelta()).toStrictEqual([{
insert: "Hello",
attributes: { link: true },
}, {
insert: "! world!",
}] as Delta<string>[]);
})

Deno.test("Rich text event", () => {
/**
* Loro text will receive rich text event in Quill Delta format
*/
const doc = new Loro();
const text = doc.getText("text");
text.insert(0, "Hello world!");
doc.commit();
let ran = false;
text.subscribe(doc, (event) => {
if (event.diff.type === "text") {
expect(event.diff.diff).toStrictEqual([{
retain: 5,
attributes: { bold: true }
}]);
ran = true;
} else {
throw new Error()
}
});
text.mark({ start: 0, end: 5 }, "bold", true);
doc.commit();
expect(ran).toBeTruthy();
});
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Loro Examples in Deno

This repo contains examples of how to use [Loro](https://loro.dev).

All the examples are under the `examples/` directory

## Running the examples

```bash
deno task test
```

27 changes: 27 additions & 0 deletions benches/text.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Loro } from "npm:[email protected]"

/**
cpu: Apple M1
runtime: deno 1.38.0 (aarch64-apple-darwin)
file:///Users/zxch3n/Code/loro-deno-examples/benches/text.bench.ts
benchmark time (avg) iter/s (min … max) p75 p99 p995
--------------------------------------------------------------------------- -----------------------------
Text 30.38 ms/iter 32.9 (27.97 ms … 42.13 ms) 30.37 ms 42.13 ms 42.13 ms
Text in raw JS string 102.6 ms/iter 9.7 (96.78 ms … 119.13 ms) 104.68 ms 119.13 ms 119.13 ms
*/

Deno.bench("Text", () => {
const doc = new Loro();
const text = doc.getText("text");
for (let i = 0; i < 30000; i++) {
text.insert(i, i.toString());
}
})

Deno.bench("Text in raw JS string", () => {
let text = "";
for (let i = 0; i < 30000; i++) {
text = text.slice(0, i) + i.toString() + text.slice(i);
}
})
6 changes: 6 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"tasks": {
"test": "deno test --allow-read",
"bench": "deno bench --allow-read"
}
}
Loading

0 comments on commit c3bdd53

Please sign in to comment.