mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-05 20:17:13 +00:00
feat: wasm api 1.0 (#521)
* feat: new 1.0 api wasm * test: add new api test
This commit is contained in:
parent
3e4005d113
commit
203d82bf3b
4 changed files with 150 additions and 6 deletions
|
@ -10,7 +10,11 @@ crate-type = ["cdylib", "rlib"]
|
|||
|
||||
[dependencies]
|
||||
js-sys = "0.3.60"
|
||||
loro-internal = { path = "../loro-internal", features = ["wasm", "counter"] }
|
||||
loro-internal = { path = "../loro-internal", features = [
|
||||
"wasm",
|
||||
"counter",
|
||||
"jsonpath",
|
||||
] }
|
||||
wasm-bindgen = "=0.2.92"
|
||||
serde-wasm-bindgen = { version = "^0.6.5" }
|
||||
wasm-bindgen-derive = "0.2.1"
|
||||
|
|
|
@ -28,7 +28,7 @@ use loro_internal::{
|
|||
};
|
||||
use rle::HasLength;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cell::RefCell, cmp::Ordering, mem::ManuallyDrop, rc::Rc, sync::Arc};
|
||||
use std::{cell::RefCell, cmp::Ordering, mem::ManuallyDrop, ops::ControlFlow, rc::Rc, sync::Arc};
|
||||
use wasm_bindgen::{__rt::IntoJsResult, prelude::*, throw_val};
|
||||
use wasm_bindgen_derive::TryFromJsValue;
|
||||
|
||||
|
@ -556,6 +556,14 @@ impl LoroDoc {
|
|||
Self(Arc::new(self.0.fork()))
|
||||
}
|
||||
|
||||
/// Creates a new LoroDoc at a specified version (Frontiers)
|
||||
#[wasm_bindgen(js_name = "forkAt")]
|
||||
pub fn fork_at(&self, frontiers: Vec<JsID>) -> JsResult<LoroDoc> {
|
||||
Ok(Self(Arc::new(
|
||||
self.0.fork_at(&ids_to_frontiers(frontiers)?),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Checkout the `DocState` to the latest version of `OpLog`.
|
||||
///
|
||||
/// > The document becomes detached during a `checkout` operation.
|
||||
|
@ -584,6 +592,47 @@ impl LoroDoc {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
#[wasm_bindgen(js_name = "travelChangeAncestors")]
|
||||
pub fn travel_change_ancestors(&self, ids: Vec<JsID>, f: js_sys::Function) -> JsResult<()> {
|
||||
let observer = observer::Observer::new(f);
|
||||
self.0
|
||||
.travel_change_ancestors(
|
||||
&ids.into_iter()
|
||||
.map(|id| js_id_to_id(id).unwrap())
|
||||
.collect::<Vec<_>>(),
|
||||
&mut |meta| {
|
||||
let res = observer
|
||||
.call1(
|
||||
&ChangeMeta {
|
||||
lamport: meta.lamport,
|
||||
length: meta.len as u32,
|
||||
peer: meta.id.peer.to_string(),
|
||||
counter: meta.id.counter,
|
||||
deps: meta
|
||||
.deps
|
||||
.iter()
|
||||
.map(|id| StringID {
|
||||
peer: id.peer.to_string(),
|
||||
counter: id.counter,
|
||||
})
|
||||
.collect(),
|
||||
timestamp: meta.timestamp as f64,
|
||||
message: meta.message,
|
||||
}
|
||||
.to_js(),
|
||||
)
|
||||
.unwrap();
|
||||
if res.as_bool().unwrap() {
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ControlFlow::Break(())
|
||||
}
|
||||
},
|
||||
)
|
||||
.map_err(|e| JsValue::from(e.to_string()))
|
||||
}
|
||||
|
||||
/// Checkout the `DocState` to a specific version.
|
||||
///
|
||||
/// > The document becomes detached during a `checkout` operation.
|
||||
|
@ -894,6 +943,47 @@ impl LoroDoc {
|
|||
})
|
||||
}
|
||||
|
||||
/// Set the commit message of the next commit
|
||||
#[wasm_bindgen(js_name = "setNextCommitMessage")]
|
||||
pub fn set_next_commit_message(&self, msg: &str) {
|
||||
self.0.set_next_commit_message(msg);
|
||||
}
|
||||
|
||||
/// Get deep value of the document with container id
|
||||
#[wasm_bindgen(js_name = "getDeepValueWithID")]
|
||||
pub fn get_deep_value_with_id(&self) -> JsValue {
|
||||
self.0.get_deep_value_with_id().into()
|
||||
}
|
||||
|
||||
/// Get the path from the root to the container
|
||||
#[wasm_bindgen(js_name = "getPathToContainer")]
|
||||
pub fn get_path_to_container(&self, id: JsContainerID) -> JsResult<Option<Array>> {
|
||||
let id: ContainerID = id.to_owned().try_into()?;
|
||||
let ans = self
|
||||
.0
|
||||
.get_path_to_container(&id)
|
||||
.map(|p| convert_container_path_to_js_value(&p));
|
||||
Ok(ans)
|
||||
}
|
||||
|
||||
/// Evaluate JSONPath against a LoroDoc
|
||||
#[wasm_bindgen(js_name = "JSONPath")]
|
||||
pub fn json_path(&self, jsonpath: &str) -> JsResult<Array> {
|
||||
let ans = Array::new();
|
||||
for v in self
|
||||
.0
|
||||
.jsonpath(jsonpath)
|
||||
.map_err(|e| JsValue::from(e.to_string()))?
|
||||
.into_iter()
|
||||
{
|
||||
ans.push(&match v {
|
||||
ValueOrHandler::Handler(h) => handler_to_js_value(h, Some(self.0.clone())),
|
||||
ValueOrHandler::Value(v) => v.into(),
|
||||
});
|
||||
}
|
||||
Ok(ans)
|
||||
}
|
||||
|
||||
/// Get the encoded version vector of the current document.
|
||||
///
|
||||
/// If you checkout to a specific version, the version vector will change.
|
||||
|
@ -1634,13 +1724,12 @@ fn container_diff_to_js_value(
|
|||
obj.into()
|
||||
}
|
||||
|
||||
fn convert_container_path_to_js_value(path: &[(ContainerID, Index)]) -> JsValue {
|
||||
fn convert_container_path_to_js_value(path: &[(ContainerID, Index)]) -> Array {
|
||||
let arr = Array::new_with_length(path.len() as u32);
|
||||
for (i, p) in path.iter().enumerate() {
|
||||
arr.set(i as u32, p.1.clone().into());
|
||||
}
|
||||
let path: JsValue = arr.into_js_result().unwrap();
|
||||
path
|
||||
arr
|
||||
}
|
||||
|
||||
/// The handler of a text container. It supports rich text CRDT.
|
||||
|
@ -1745,6 +1834,12 @@ impl LoroText {
|
|||
self.handler.update(text);
|
||||
}
|
||||
|
||||
/// Update the current text based on the provided text line by line.
|
||||
#[wasm_bindgen(js_name = "updateByLine")]
|
||||
pub fn update_by_line(&self, text: &str) {
|
||||
self.handler.update_by_line(text);
|
||||
}
|
||||
|
||||
/// Insert some string at index.
|
||||
///
|
||||
/// @example
|
||||
|
@ -3693,6 +3788,12 @@ impl LoroTree {
|
|||
pub fn disable_fractional_index(&self) {
|
||||
self.handler.disable_fractional_index();
|
||||
}
|
||||
|
||||
/// Whether the tree enables the fractional index generation.
|
||||
#[wasm_bindgen(js_name = "isFractionalIndexEnabled")]
|
||||
pub fn is_fractional_index_enabled(&self) -> bool {
|
||||
self.handler.is_fractional_index_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LoroTree {
|
||||
|
@ -4035,6 +4136,10 @@ impl UndoManager {
|
|||
self.undo.set_on_pop(None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.undo.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this function to throw an error after the micro task.
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
encodeFrontiers,
|
||||
decodeFrontiers,
|
||||
} from "../src";
|
||||
import { m } from "vitest/dist/reporters-yx5ZTtEV";
|
||||
|
||||
it("basic example", () => {
|
||||
const doc = new LoroDoc();
|
||||
|
@ -619,3 +620,38 @@ it("can encode/decode frontiers", () => {
|
|||
const decoded = decodeFrontiers(encoded);
|
||||
expect(decoded).toStrictEqual(frontiers);
|
||||
})
|
||||
|
||||
it("travel changes", () => {
|
||||
let doc = new LoroDoc();
|
||||
doc.setPeerId(1);
|
||||
doc.getText("text").insert(0, "abc");
|
||||
doc.commit();
|
||||
let n = 0;
|
||||
doc.travelChangeAncestors([{ peer: "1", counter: 0 }], (meta: any) => {
|
||||
n += 1;
|
||||
return true
|
||||
})
|
||||
expect(n).toBe(1);
|
||||
})
|
||||
|
||||
it("get path to container", () => {
|
||||
const doc = new LoroDoc();
|
||||
const map = doc.getMap("map");
|
||||
const list = map.setContainer("list", new LoroList());
|
||||
const path = doc.getPathToContainer(list.id);
|
||||
expect(path).toStrictEqual(["map", "list"])
|
||||
})
|
||||
|
||||
it("json path", () => {
|
||||
const doc = new LoroDoc();
|
||||
const map = doc.getMap("map");
|
||||
map.set("key", "value");
|
||||
const books = map.setContainer("books", new LoroList());
|
||||
const book = books.insertContainer(0, new LoroMap());
|
||||
book.set("title", "1984");
|
||||
book.set("author", "George Orwell");
|
||||
const path = "$['map'].books[0].title";
|
||||
const result = doc.JSONPath(path);
|
||||
expect(result.length).toBe(1);
|
||||
expect(result).toStrictEqual(["1984"])
|
||||
})
|
|
@ -246,7 +246,6 @@ describe("type", () => {
|
|||
describe("list stable position", () => {
|
||||
it("basic tests", () => {
|
||||
const loro = new LoroDoc();
|
||||
loro.oplogFrontiers
|
||||
const list = loro.getList("list");
|
||||
list.insert(0, "a");
|
||||
const pos0 = list.getCursor(0);
|
||||
|
|
Loading…
Reference in a new issue