loro-crdt 0.15.3 โ 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/loro.d.ts +4 -4
- package/dist/loro.js +2 -2
- package/dist/loro.js.map +1 -1
- package/dist/loro.mjs +1 -1
- package/dist/loro.mjs.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +30 -16
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- c12c2b9: Movable Tree Children & Undo
|
|
8
|
+
|
|
9
|
+
#### ๐ Bug Fixes
|
|
10
|
+
|
|
11
|
+
- Refine error message on corrupted data (#356)
|
|
12
|
+
- Add MovableList to CONTAINER_TYPES (#359)
|
|
13
|
+
- Better jitter for fractional index (#360)
|
|
14
|
+
|
|
15
|
+
#### ๐งช Testing
|
|
16
|
+
|
|
17
|
+
- Add compatibility tests (#357)
|
|
18
|
+
|
|
19
|
+
#### Feat
|
|
20
|
+
|
|
21
|
+
- Make the encoding format forward and backward compatible (#329)
|
|
22
|
+
- Undo (#361)
|
|
23
|
+
- Use fractional index to order the children of the tree (#298)
|
|
24
|
+
|
|
25
|
+
#### ๐ Bug Fixes
|
|
26
|
+
|
|
27
|
+
- Tree fuzz sort value (#351)
|
|
28
|
+
- Upgrade wasm-bindgen to fix str free err (#353)
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [c12c2b9]
|
|
33
|
+
- loro-wasm@0.16.0
|
|
34
|
+
|
|
3
35
|
## 0.15.3
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/dist/loro.d.ts
CHANGED
|
@@ -173,7 +173,7 @@ declare module "loro-wasm" {
|
|
|
173
173
|
* const map = doc.getMap("map");
|
|
174
174
|
* ```
|
|
175
175
|
*/
|
|
176
|
-
getMap<Key extends
|
|
176
|
+
getMap<Key extends keyof T | ContainerID>(name: Key): T[Key] extends LoroMap ? T[Key] : LoroMap;
|
|
177
177
|
/**
|
|
178
178
|
* Get a LoroList by container id
|
|
179
179
|
*
|
|
@@ -188,7 +188,7 @@ declare module "loro-wasm" {
|
|
|
188
188
|
* const list = doc.getList("list");
|
|
189
189
|
* ```
|
|
190
190
|
*/
|
|
191
|
-
getList<Key extends
|
|
191
|
+
getList<Key extends keyof T | ContainerID>(name: Key): T[Key] extends LoroList ? T[Key] : LoroList;
|
|
192
192
|
/**
|
|
193
193
|
* Get a LoroMovableList by container id
|
|
194
194
|
*
|
|
@@ -203,7 +203,7 @@ declare module "loro-wasm" {
|
|
|
203
203
|
* const list = doc.getList("list");
|
|
204
204
|
* ```
|
|
205
205
|
*/
|
|
206
|
-
getMovableList<Key extends
|
|
206
|
+
getMovableList<Key extends keyof T | ContainerID>(name: Key): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;
|
|
207
207
|
/**
|
|
208
208
|
* Get a LoroTree by container id
|
|
209
209
|
*
|
|
@@ -218,7 +218,7 @@ declare module "loro-wasm" {
|
|
|
218
218
|
* const tree = doc.getTree("tree");
|
|
219
219
|
* ```
|
|
220
220
|
*/
|
|
221
|
-
getTree<Key extends
|
|
221
|
+
getTree<Key extends keyof T | ContainerID>(name: Key): T[Key] extends LoroTree ? T[Key] : LoroTree;
|
|
222
222
|
getText(key: string | ContainerID): LoroText;
|
|
223
223
|
}
|
|
224
224
|
interface LoroList<T = unknown> {
|
package/dist/loro.js
CHANGED
|
@@ -84,7 +84,7 @@ class Awareness {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
const CONTAINER_TYPES = ["Map", "Text", "List", "Tree"];
|
|
87
|
+
const CONTAINER_TYPES = ["Map", "Text", "List", "Tree", "MovableList"];
|
|
88
88
|
function isContainerId(s) {
|
|
89
89
|
return s.startsWith("cid:");
|
|
90
90
|
}
|
|
@@ -114,7 +114,7 @@ exports.getType = getType;
|
|
|
114
114
|
exports.isContainer = isContainer;
|
|
115
115
|
exports.isContainerId = isContainerId;
|
|
116
116
|
Object.keys(loroWasm).forEach(function (k) {
|
|
117
|
-
if (k !== 'default' && !
|
|
117
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
118
118
|
enumerable: true,
|
|
119
119
|
get: function () { return loroWasm[k]; }
|
|
120
120
|
});
|
package/dist/loro.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loro.js","sources":["../src/awareness.ts","../src/index.ts"],"sourcesContent":["import { AwarenessWasm, PeerID, Value } from \"loro-wasm\";\n\nexport type AwarenessListener = (\n arg: { updated: PeerID[]; added: PeerID[]; removed: PeerID[] },\n origin: \"local\" | \"timeout\" | \"remote\" | string,\n) => void;\n\n/**\n * Awareness is a structure that allows to track the ephemeral state of the peers.\n *\n * If we don't receive a state update from a peer within the timeout, we will remove their state.\n * The timeout is in milliseconds. This can be used to handle the off-line state of a peer.\n */\nexport class Awareness<T extends Value = Value> {\n inner: AwarenessWasm<T>;\n private peer: PeerID;\n private timer: number | undefined;\n private timeout: number;\n private listeners: Set<AwarenessListener> = new Set();\n constructor(peer: PeerID, timeout: number = 30000) {\n this.inner = new AwarenessWasm(peer, timeout);\n this.peer = peer;\n this.timeout = timeout;\n }\n\n apply(bytes: Uint8Array, origin = \"remote\") {\n const { updated, added } = this.inner.apply(bytes);\n this.listeners.forEach((listener) => {\n listener({ updated, added, removed: [] }, origin);\n });\n\n this.startTimerIfNotEmpty();\n }\n\n setLocalState(state: T) {\n const wasEmpty = this.inner.getState(this.peer) == null;\n this.inner.setLocalState(state);\n if (wasEmpty) {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [], added: [this.inner.peer()], removed: [] },\n \"local\",\n );\n });\n } else {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [this.inner.peer()], added: [], removed: [] },\n \"local\",\n );\n });\n }\n\n this.startTimerIfNotEmpty();\n }\n\n getLocalState(): T | undefined {\n return this.inner.getState(this.peer);\n }\n\n getAllStates(): Record<PeerID, T> {\n return this.inner.getAllStates();\n }\n\n encode(peers: PeerID[]): Uint8Array {\n return this.inner.encode(peers);\n }\n\n encodeAll(): Uint8Array {\n return this.inner.encodeAll();\n }\n\n addListener(listener: AwarenessListener) {\n this.listeners.add(listener);\n }\n\n removeListener(listener: AwarenessListener) {\n this.listeners.delete(listener);\n }\n\n peers(): PeerID[] {\n return this.inner.peers();\n }\n\n destroy() {\n clearInterval(this.timer);\n this.listeners.clear();\n }\n\n private startTimerIfNotEmpty() {\n if (this.inner.isEmpty() || this.timer != null) {\n return;\n }\n\n this.timer = setInterval(() => {\n const removed = this.inner.removeOutdated();\n if (removed.length > 0) {\n this.listeners.forEach((listener) => {\n listener({ updated: [], added: [], removed }, \"timeout\");\n });\n }\n if (this.inner.isEmpty()) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n }, this.timeout / 2) as unknown as number;\n }\n}\n","export * from \"loro-wasm\";\nimport {\n Container,\n ContainerID,\n Delta,\n Loro,\n LoroList,\n LoroMap,\n LoroText,\n LoroTree,\n OpId,\n TreeID,\n Value,\n} from \"loro-wasm\";\nexport { Awareness } from \"./awareness\";\n\nexport type Frontiers = OpId[];\n\n/**\n * Represents a path to identify the exact location of an event's target.\n * The path is composed of numbers (e.g., indices of a list container) strings\n * (e.g., keys of a map container) and TreeID (the node of a tree container),\n * indicating the absolute position of the event's source within a loro document.\n */\nexport type Path = (number | string | TreeID)[];\n\n/**\n * A batch of events that created by a single `import`/`transaction`/`checkout`.\n *\n * @prop by - How the event is triggered.\n * @prop origin - (Optional) Provides information about the origin of the event.\n * @prop diff - Contains the differential information related to the event.\n * @prop target - Identifies the container ID of the event's target.\n * @prop path - Specifies the absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\nexport interface LoroEventBatch {\n /**\n * How the event is triggered.\n *\n * - `local`: The event is triggered by a local transaction.\n * - `import`: The event is triggered by an import operation.\n * - `checkout`: The event is triggered by a checkout operation.\n */\n by: \"local\" | \"import\" | \"checkout\";\n origin?: string;\n /**\n * The container ID of the current event receiver.\n * It's undefined if the subscriber is on the root document.\n */\n currentTarget?: ContainerID;\n events: LoroEvent[];\n}\n\n/**\n * The concrete event of Loro.\n */\nexport interface LoroEvent {\n /**\n * The container ID of the event's target.\n */\n target: ContainerID;\n diff: Diff;\n /**\n * The absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\n path: Path;\n}\n\nexport type ListDiff = {\n type: \"list\";\n diff: Delta<(Value | Container)[]>[];\n};\n\nexport type TextDiff = {\n type: \"text\";\n diff: Delta<string>[];\n};\n\nexport type MapDiff = {\n type: \"map\";\n updated: Record<string, Value | Container | undefined>;\n};\n\nexport type TreeDiffItem =\n | { target: TreeID; action: \"create\"; parent: TreeID | undefined; index: number; position: string }\n | { target: TreeID; action: \"delete\" }\n | { target: TreeID; action: \"move\"; parent: TreeID | undefined; index: number; position: string };\n\nexport type TreeDiff = {\n type: \"tree\";\n diff: TreeDiffItem[];\n};\n\nexport type Diff = ListDiff | TextDiff | MapDiff | TreeDiff;\n\ninterface Listener {\n (event: LoroEventBatch): void;\n}\n\nconst CONTAINER_TYPES = [\"Map\", \"Text\", \"List\", \"Tree\"];\n\nexport function isContainerId(s: string): s is ContainerID {\n return s.startsWith(\"cid:\");\n}\n\nexport { Loro };\n\n/** Whether the value is a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * isContainer(map); // true\n * isContainer(list); // true\n * isContainer(text); // true\n * isContainer(123); // false\n * isContainer(\"123\"); // false\n * isContainer({}); // false\n */\nexport function isContainer(value: any): value is Container {\n if (typeof value !== \"object\" || value == null) {\n return false;\n }\n\n const p = Object.getPrototypeOf(value);\n if (p == null || typeof p !== \"object\" || typeof p[\"kind\"] !== \"function\") {\n return false;\n }\n\n return CONTAINER_TYPES.includes(value.kind());\n}\n\n/** Get the type of a value that may be a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * getType(map); // \"Map\"\n * getType(list); // \"List\"\n * getType(text); // \"Text\"\n * getType(123); // \"Json\"\n * getType(\"123\"); // \"Json\"\n * getType({}); // \"Json\"\n * ```\n */\nexport function getType<T>(\n value: T,\n): T extends LoroText ? \"Text\"\n : T extends LoroMap<any> ? \"Map\"\n : T extends LoroTree<any> ? \"Tree\"\n : T extends LoroList<any> ? \"List\"\n : \"Json\" {\n if (isContainer(value)) {\n return value.kind() as unknown as any;\n }\n\n return \"Json\" as any;\n}\n\ndeclare module \"loro-wasm\" {\n interface Loro {\n subscribe(listener: Listener): number;\n }\n\n interface Loro<\n T extends Record<string, Container> = Record<string, Container>,\n > {\n /**\n * Get a LoroMap by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * ```\n */\n getMap<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMap ? T[Key] : LoroMap;\n /**\n * Get a LoroList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getList<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroList ? T[Key] : LoroList;\n /**\n * Get a LoroMovableList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getMovableList<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;\n /**\n * Get a LoroTree by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const tree = doc.getTree(\"tree\");\n * ```\n */\n getTree<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroTree ? T[Key] : LoroTree;\n getText(key: string | ContainerID): LoroText;\n }\n\n interface LoroList<T = unknown> {\n new (): LoroList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroList<T>;\n }\n\n interface LoroMovableList<T = unknown> {\n new (): LoroMovableList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMoableList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroMovableList<T>;\n /**\n * Set the value at the given position.\n *\n * It's different from `delete` + `insert` that it will replace the value at the position.\n *\n * For example, if you have a list `[1, 2, 3]`, and you call `set(1, 100)`, the list will be `[1, 100, 3]`.\n * If concurrently someone call `set(1, 200)`, the list will be `[1, 200, 3]` or `[1, 100, 3]`.\n *\n * But if you use `delete` + `insert` to simulate the set operation, they may create redundant operations\n * and the final result will be `[1, 100, 200, 3]` or `[1, 200, 100, 3]`.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.set(1, \"bar\");\n * console.log(list.value); // [100, \"bar\", true];\n * ```\n */\n set<V extends T>(pos: number, value: Exclude<V, Container>): void;\n /**\n * Set a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.setContainer(0, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [\"Hello\"];\n * ```\n */\n setContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n }\n\n interface LoroMap<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroMap<T>;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object returned is a new js object each time because it need to cross\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n getOrCreateContainer<C extends Container>(key: string, child: C): C;\n /**\n * Set the key with a container.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const text = map.setContainer(\"text\", new LoroText());\n * const list = map.setContainer(\"list\", new LoroText());\n * ```\n */\n setContainer<C extends Container, Key extends keyof T>(\n key: Key,\n child: C,\n ): NonNullableType<T[Key]> extends C ? NonNullableType<T[Key]> : C;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object/value returned is a new js object/value each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n get<Key extends keyof T>(key: Key): T[Key];\n /**\n * Set the key with the value.\n *\n * If the value of the key is exist, the old value will be updated.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * map.set(\"foo\", \"baz\");\n * ```\n */\n set<Key extends keyof T, V extends T[Key]>(\n key: Key,\n value: Exclude<V, Container>,\n ): void;\n delete(key: string): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroText {\n new (): LoroText;\n insert(pos: number, text: string): void;\n delete(pos: number, len: number): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTree<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroTree<T>;\n createNode(parent?: TreeID, index?: number ): LoroTreeNode<T>;\n move(target: TreeID, parent?: TreeID, index?: number): void;\n delete(target: TreeID): void;\n has(target: TreeID): boolean;\n getNodeByID(target: TreeID): LoroTreeNode;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTreeNode<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n /**\n * Get the associated metadata map container of a tree node.\n */\n readonly data: LoroMap<T>;\n createNode(index?: number): LoroTreeNode<T>;\n move(parent?: LoroTreeNode<T>, index?: number): void;\n parent(): LoroTreeNode<T> | undefined;\n children(): Array<LoroTreeNode<T>>;\n }\n\n interface AwarenessWasm<\n T extends Value = Value,\n > {\n getState(peer: PeerID): T | undefined;\n getTimestamp(peer: PeerID): number | undefined;\n getAllStates(): Record<PeerID, T>;\n setLocalState(value: T): void;\n removeOutdated(): PeerID[];\n }\n}\n\ntype NonNullableType<T> = Exclude<T, null | undefined>;\n"],"names":["AwarenessWasm"],"mappings":";;;;AAaO,MAAM,SAAmC,CAAA;AAAA,EAC9C,KAAA,CAAA;AAAA,EACQ,IAAA,CAAA;AAAA,EACA,KAAA,CAAA;AAAA,EACA,OAAA,CAAA;AAAA,EACA,SAAA,uBAAwC,GAAI,EAAA,CAAA;AAAA,EACpD,WAAA,CAAY,IAAc,EAAA,OAAA,GAAkB,GAAO,EAAA;AACjD,IAAA,IAAA,CAAK,KAAQ,GAAA,IAAIA,sBAAc,CAAA,IAAA,EAAM,OAAO,CAAA,CAAA;AAC5C,IAAA,IAAA,CAAK,IAAO,GAAA,IAAA,CAAA;AACZ,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA,CAAA;AAAA,GACjB;AAAA,EAEA,KAAA,CAAM,KAAmB,EAAA,MAAA,GAAS,QAAU,EAAA;AAC1C,IAAA,MAAM,EAAE,OAAS,EAAA,KAAA,KAAU,IAAK,CAAA,KAAA,CAAM,MAAM,KAAK,CAAA,CAAA;AACjD,IAAK,IAAA,CAAA,SAAA,CAAU,OAAQ,CAAA,CAAC,QAAa,KAAA;AACnC,MAAA,QAAA,CAAS,EAAE,OAAS,EAAA,KAAA,EAAO,SAAS,EAAC,IAAK,MAAM,CAAA,CAAA;AAAA,KACjD,CAAA,CAAA;AAED,IAAA,IAAA,CAAK,oBAAqB,EAAA,CAAA;AAAA,GAC5B;AAAA,EAEA,cAAc,KAAU,EAAA;AACtB,IAAA,MAAM,WAAW,IAAK,CAAA,KAAA,CAAM,QAAS,CAAA,IAAA,CAAK,IAAI,CAAK,IAAA,IAAA,CAAA;AACnD,IAAK,IAAA,CAAA,KAAA,CAAM,cAAc,KAAK,CAAA,CAAA;AAC9B,IAAA,IAAI,QAAU,EAAA;AACZ,MAAK,IAAA,CAAA,SAAA,CAAU,OAAQ,CAAA,CAAC,QAAa,KAAA;AACnC,QAAA,QAAA;AAAA,UACE,EAAE,OAAA,EAAS,EAAC,EAAG,KAAO,EAAA,CAAC,IAAK,CAAA,KAAA,CAAM,IAAK,EAAC,CAAG,EAAA,OAAA,EAAS,EAAG,EAAA;AAAA,UACvD,OAAA;AAAA,SACF,CAAA;AAAA,OACD,CAAA,CAAA;AAAA,KACI,MAAA;AACL,MAAK,IAAA,CAAA,SAAA,CAAU,OAAQ,CAAA,CAAC,QAAa,KAAA;AACnC,QAAA,QAAA;AAAA,UACE,EAAE,OAAA,EAAS,CAAC,IAAA,CAAK,KAAM,CAAA,IAAA,EAAM,CAAA,EAAG,KAAO,EAAA,EAAI,EAAA,OAAA,EAAS,EAAG,EAAA;AAAA,UACvD,OAAA;AAAA,SACF,CAAA;AAAA,OACD,CAAA,CAAA;AAAA,KACH;AAEA,IAAA,IAAA,CAAK,oBAAqB,EAAA,CAAA;AAAA,GAC5B;AAAA,EAEA,aAA+B,GAAA;AAC7B,IAAA,OAAO,IAAK,CAAA,KAAA,CAAM,QAAS,CAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,GACtC;AAAA,EAEA,YAAkC,GAAA;AAChC,IAAO,OAAA,IAAA,CAAK,MAAM,YAAa,EAAA,CAAA;AAAA,GACjC;AAAA,EAEA,OAAO,KAA6B,EAAA;AAClC,IAAO,OAAA,IAAA,CAAK,KAAM,CAAA,MAAA,CAAO,KAAK,CAAA,CAAA;AAAA,GAChC;AAAA,EAEA,SAAwB,GAAA;AACtB,IAAO,OAAA,IAAA,CAAK,MAAM,SAAU,EAAA,CAAA;AAAA,GAC9B;AAAA,EAEA,YAAY,QAA6B,EAAA;AACvC,IAAK,IAAA,CAAA,SAAA,CAAU,IAAI,QAAQ,CAAA,CAAA;AAAA,GAC7B;AAAA,EAEA,eAAe,QAA6B,EAAA;AAC1C,IAAK,IAAA,CAAA,SAAA,CAAU,OAAO,QAAQ,CAAA,CAAA;AAAA,GAChC;AAAA,EAEA,KAAkB,GAAA;AAChB,IAAO,OAAA,IAAA,CAAK,MAAM,KAAM,EAAA,CAAA;AAAA,GAC1B;AAAA,EAEA,OAAU,GAAA;AACR,IAAA,aAAA,CAAc,KAAK,KAAK,CAAA,CAAA;AACxB,IAAA,IAAA,CAAK,UAAU,KAAM,EAAA,CAAA;AAAA,GACvB;AAAA,EAEQ,oBAAuB,GAAA;AAC7B,IAAA,IAAI,KAAK,KAAM,CAAA,OAAA,EAAa,IAAA,IAAA,CAAK,SAAS,IAAM,EAAA;AAC9C,MAAA,OAAA;AAAA,KACF;AAEA,IAAK,IAAA,CAAA,KAAA,GAAQ,YAAY,MAAM;AAC7B,MAAM,MAAA,OAAA,GAAU,IAAK,CAAA,KAAA,CAAM,cAAe,EAAA,CAAA;AAC1C,MAAI,IAAA,OAAA,CAAQ,SAAS,CAAG,EAAA;AACtB,QAAK,IAAA,CAAA,SAAA,CAAU,OAAQ,CAAA,CAAC,QAAa,KAAA;AACnC,UAAS,QAAA,CAAA,EAAE,SAAS,EAAC,EAAG,OAAO,EAAC,EAAG,OAAQ,EAAA,EAAG,SAAS,CAAA,CAAA;AAAA,SACxD,CAAA,CAAA;AAAA,OACH;AACA,MAAI,IAAA,IAAA,CAAK,KAAM,CAAA,OAAA,EAAW,EAAA;AACxB,QAAA,aAAA,CAAc,KAAK,KAAK,CAAA,CAAA;AACxB,QAAA,IAAA,CAAK,KAAQ,GAAA,KAAA,CAAA,CAAA;AAAA,OACf;AAAA,KACF,EAAG,IAAK,CAAA,OAAA,GAAU,CAAC,CAAA,CAAA;AAAA,GACrB;AACF;;ACRA,MAAM,eAAkB,GAAA,CAAC,KAAO,EAAA,MAAA,EAAQ,QAAQ,MAAM,CAAA,CAAA;AAE/C,SAAS,cAAc,CAA6B,EAAA;AACzD,EAAO,OAAA,CAAA,CAAE,WAAW,MAAM,CAAA,CAAA;AAC5B,CAAA;AAoBO,SAAS,YAAY,KAAgC,EAAA;AAC1D,EAAA,IAAI,OAAO,KAAA,KAAU,QAAY,IAAA,KAAA,IAAS,IAAM,EAAA;AAC9C,IAAO,OAAA,KAAA,CAAA;AAAA,GACT;AAEA,EAAM,MAAA,CAAA,GAAI,MAAO,CAAA,cAAA,CAAe,KAAK,CAAA,CAAA;AACrC,EAAI,IAAA,CAAA,IAAK,QAAQ,OAAO,CAAA,KAAM,YAAY,OAAO,CAAA,CAAE,MAAM,CAAA,KAAM,UAAY,EAAA;AACzE,IAAO,OAAA,KAAA,CAAA;AAAA,GACT;AAEA,EAAA,OAAO,eAAgB,CAAA,QAAA,CAAS,KAAM,CAAA,IAAA,EAAM,CAAA,CAAA;AAC9C,CAAA;AAmBO,SAAS,QACd,KAKS,EAAA;AACT,EAAI,IAAA,WAAA,CAAY,KAAK,CAAG,EAAA;AACtB,IAAA,OAAO,MAAM,IAAK,EAAA,CAAA;AAAA,GACpB;AAEA,EAAO,OAAA,MAAA,CAAA;AACT;;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"loro.js","sources":["../src/awareness.ts","../src/index.ts"],"sourcesContent":["import { AwarenessWasm, PeerID, Value } from \"loro-wasm\";\n\nexport type AwarenessListener = (\n arg: { updated: PeerID[]; added: PeerID[]; removed: PeerID[] },\n origin: \"local\" | \"timeout\" | \"remote\" | string,\n) => void;\n\n/**\n * Awareness is a structure that allows to track the ephemeral state of the peers.\n *\n * If we don't receive a state update from a peer within the timeout, we will remove their state.\n * The timeout is in milliseconds. This can be used to handle the off-line state of a peer.\n */\nexport class Awareness<T extends Value = Value> {\n inner: AwarenessWasm<T>;\n private peer: PeerID;\n private timer: number | undefined;\n private timeout: number;\n private listeners: Set<AwarenessListener> = new Set();\n constructor(peer: PeerID, timeout: number = 30000) {\n this.inner = new AwarenessWasm(peer, timeout);\n this.peer = peer;\n this.timeout = timeout;\n }\n\n apply(bytes: Uint8Array, origin = \"remote\") {\n const { updated, added } = this.inner.apply(bytes);\n this.listeners.forEach((listener) => {\n listener({ updated, added, removed: [] }, origin);\n });\n\n this.startTimerIfNotEmpty();\n }\n\n setLocalState(state: T) {\n const wasEmpty = this.inner.getState(this.peer) == null;\n this.inner.setLocalState(state);\n if (wasEmpty) {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [], added: [this.inner.peer()], removed: [] },\n \"local\",\n );\n });\n } else {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [this.inner.peer()], added: [], removed: [] },\n \"local\",\n );\n });\n }\n\n this.startTimerIfNotEmpty();\n }\n\n getLocalState(): T | undefined {\n return this.inner.getState(this.peer);\n }\n\n getAllStates(): Record<PeerID, T> {\n return this.inner.getAllStates();\n }\n\n encode(peers: PeerID[]): Uint8Array {\n return this.inner.encode(peers);\n }\n\n encodeAll(): Uint8Array {\n return this.inner.encodeAll();\n }\n\n addListener(listener: AwarenessListener) {\n this.listeners.add(listener);\n }\n\n removeListener(listener: AwarenessListener) {\n this.listeners.delete(listener);\n }\n\n peers(): PeerID[] {\n return this.inner.peers();\n }\n\n destroy() {\n clearInterval(this.timer);\n this.listeners.clear();\n }\n\n private startTimerIfNotEmpty() {\n if (this.inner.isEmpty() || this.timer != null) {\n return;\n }\n\n this.timer = setInterval(() => {\n const removed = this.inner.removeOutdated();\n if (removed.length > 0) {\n this.listeners.forEach((listener) => {\n listener({ updated: [], added: [], removed }, \"timeout\");\n });\n }\n if (this.inner.isEmpty()) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n }, this.timeout / 2) as unknown as number;\n }\n}\n","export * from \"loro-wasm\";\nimport {\n Container,\n ContainerID,\n Delta,\n Loro,\n LoroList,\n LoroMap,\n LoroText,\n LoroTree,\n OpId,\n TreeID,\n Value,\n} from \"loro-wasm\";\nexport { Awareness } from \"./awareness\";\n\nexport type Frontiers = OpId[];\n\n/**\n * Represents a path to identify the exact location of an event's target.\n * The path is composed of numbers (e.g., indices of a list container) strings\n * (e.g., keys of a map container) and TreeID (the node of a tree container),\n * indicating the absolute position of the event's source within a loro document.\n */\nexport type Path = (number | string | TreeID)[];\n\n/**\n * A batch of events that created by a single `import`/`transaction`/`checkout`.\n *\n * @prop by - How the event is triggered.\n * @prop origin - (Optional) Provides information about the origin of the event.\n * @prop diff - Contains the differential information related to the event.\n * @prop target - Identifies the container ID of the event's target.\n * @prop path - Specifies the absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\nexport interface LoroEventBatch {\n /**\n * How the event is triggered.\n *\n * - `local`: The event is triggered by a local transaction.\n * - `import`: The event is triggered by an import operation.\n * - `checkout`: The event is triggered by a checkout operation.\n */\n by: \"local\" | \"import\" | \"checkout\";\n origin?: string;\n /**\n * The container ID of the current event receiver.\n * It's undefined if the subscriber is on the root document.\n */\n currentTarget?: ContainerID;\n events: LoroEvent[];\n}\n\n/**\n * The concrete event of Loro.\n */\nexport interface LoroEvent {\n /**\n * The container ID of the event's target.\n */\n target: ContainerID;\n diff: Diff;\n /**\n * The absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\n path: Path;\n}\n\nexport type ListDiff = {\n type: \"list\";\n diff: Delta<(Value | Container)[]>[];\n};\n\nexport type TextDiff = {\n type: \"text\";\n diff: Delta<string>[];\n};\n\nexport type MapDiff = {\n type: \"map\";\n updated: Record<string, Value | Container | undefined>;\n};\n\nexport type TreeDiffItem =\n | {\n target: TreeID;\n action: \"create\";\n parent: TreeID | undefined;\n index: number;\n position: string;\n }\n | { target: TreeID; action: \"delete\" }\n | {\n target: TreeID;\n action: \"move\";\n parent: TreeID | undefined;\n index: number;\n position: string;\n };\n\nexport type TreeDiff = {\n type: \"tree\";\n diff: TreeDiffItem[];\n};\n\nexport type Diff = ListDiff | TextDiff | MapDiff | TreeDiff;\n\ninterface Listener {\n (event: LoroEventBatch): void;\n}\n\nconst CONTAINER_TYPES = [\"Map\", \"Text\", \"List\", \"Tree\", \"MovableList\"];\n\nexport function isContainerId(s: string): s is ContainerID {\n return s.startsWith(\"cid:\");\n}\n\nexport { Loro };\n\n/** Whether the value is a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * isContainer(map); // true\n * isContainer(list); // true\n * isContainer(text); // true\n * isContainer(123); // false\n * isContainer(\"123\"); // false\n * isContainer({}); // false\n */\nexport function isContainer(value: any): value is Container {\n if (typeof value !== \"object\" || value == null) {\n return false;\n }\n\n const p = Object.getPrototypeOf(value);\n if (p == null || typeof p !== \"object\" || typeof p[\"kind\"] !== \"function\") {\n return false;\n }\n\n return CONTAINER_TYPES.includes(value.kind());\n}\n\n/** Get the type of a value that may be a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * getType(map); // \"Map\"\n * getType(list); // \"List\"\n * getType(text); // \"Text\"\n * getType(123); // \"Json\"\n * getType(\"123\"); // \"Json\"\n * getType({}); // \"Json\"\n * ```\n */\nexport function getType<T>(\n value: T,\n): T extends LoroText\n ? \"Text\"\n : T extends LoroMap<any>\n ? \"Map\"\n : T extends LoroTree<any>\n ? \"Tree\"\n : T extends LoroList<any>\n ? \"List\"\n : \"Json\" {\n if (isContainer(value)) {\n return value.kind() as unknown as any;\n }\n\n return \"Json\" as any;\n}\n\ndeclare module \"loro-wasm\" {\n interface Loro {\n subscribe(listener: Listener): number;\n }\n\n interface Loro<\n T extends Record<string, Container> = Record<string, Container>,\n > {\n /**\n * Get a LoroMap by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * ```\n */\n getMap<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMap ? T[Key] : LoroMap;\n /**\n * Get a LoroList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getList<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroList ? T[Key] : LoroList;\n /**\n * Get a LoroMovableList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getMovableList<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;\n /**\n * Get a LoroTree by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const tree = doc.getTree(\"tree\");\n * ```\n */\n getTree<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroTree ? T[Key] : LoroTree;\n getText(key: string | ContainerID): LoroText;\n }\n\n interface LoroList<T = unknown> {\n new (): LoroList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroList<T>;\n }\n\n interface LoroMovableList<T = unknown> {\n new (): LoroMovableList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMoableList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroMovableList<T>;\n /**\n * Set the value at the given position.\n *\n * It's different from `delete` + `insert` that it will replace the value at the position.\n *\n * For example, if you have a list `[1, 2, 3]`, and you call `set(1, 100)`, the list will be `[1, 100, 3]`.\n * If concurrently someone call `set(1, 200)`, the list will be `[1, 200, 3]` or `[1, 100, 3]`.\n *\n * But if you use `delete` + `insert` to simulate the set operation, they may create redundant operations\n * and the final result will be `[1, 100, 200, 3]` or `[1, 200, 100, 3]`.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.set(1, \"bar\");\n * console.log(list.value); // [100, \"bar\", true];\n * ```\n */\n set<V extends T>(pos: number, value: Exclude<V, Container>): void;\n /**\n * Set a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.setContainer(0, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [\"Hello\"];\n * ```\n */\n setContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n }\n\n interface LoroMap<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroMap<T>;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object returned is a new js object each time because it need to cross\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n getOrCreateContainer<C extends Container>(key: string, child: C): C;\n /**\n * Set the key with a container.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const text = map.setContainer(\"text\", new LoroText());\n * const list = map.setContainer(\"list\", new LoroText());\n * ```\n */\n setContainer<C extends Container, Key extends keyof T>(\n key: Key,\n child: C,\n ): NonNullableType<T[Key]> extends C ? NonNullableType<T[Key]> : C;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object/value returned is a new js object/value each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n get<Key extends keyof T>(key: Key): T[Key];\n /**\n * Set the key with the value.\n *\n * If the value of the key is exist, the old value will be updated.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * map.set(\"foo\", \"baz\");\n * ```\n */\n set<Key extends keyof T, V extends T[Key]>(\n key: Key,\n value: Exclude<V, Container>,\n ): void;\n delete(key: string): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroText {\n new (): LoroText;\n insert(pos: number, text: string): void;\n delete(pos: number, len: number): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTree<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroTree<T>;\n createNode(parent?: TreeID, index?: number): LoroTreeNode<T>;\n move(target: TreeID, parent?: TreeID, index?: number): void;\n delete(target: TreeID): void;\n has(target: TreeID): boolean;\n getNodeByID(target: TreeID): LoroTreeNode;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTreeNode<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n /**\n * Get the associated metadata map container of a tree node.\n */\n readonly data: LoroMap<T>;\n createNode(index?: number): LoroTreeNode<T>;\n move(parent?: LoroTreeNode<T>, index?: number): void;\n parent(): LoroTreeNode<T> | undefined;\n children(): Array<LoroTreeNode<T>>;\n }\n\n interface AwarenessWasm<T extends Value = Value> {\n getState(peer: PeerID): T | undefined;\n getTimestamp(peer: PeerID): number | undefined;\n getAllStates(): Record<PeerID, T>;\n setLocalState(value: T): void;\n removeOutdated(): PeerID[];\n }\n}\n\ntype NonNullableType<T> = Exclude<T, null | undefined>;\n"],"names":["AwarenessWasm"],"mappings":"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"}
|
package/dist/loro.mjs
CHANGED
package/dist/loro.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loro.mjs","sources":["../src/awareness.ts","../src/index.ts"],"sourcesContent":["import { AwarenessWasm, PeerID, Value } from \"loro-wasm\";\n\nexport type AwarenessListener = (\n arg: { updated: PeerID[]; added: PeerID[]; removed: PeerID[] },\n origin: \"local\" | \"timeout\" | \"remote\" | string,\n) => void;\n\n/**\n * Awareness is a structure that allows to track the ephemeral state of the peers.\n *\n * If we don't receive a state update from a peer within the timeout, we will remove their state.\n * The timeout is in milliseconds. This can be used to handle the off-line state of a peer.\n */\nexport class Awareness<T extends Value = Value> {\n inner: AwarenessWasm<T>;\n private peer: PeerID;\n private timer: number | undefined;\n private timeout: number;\n private listeners: Set<AwarenessListener> = new Set();\n constructor(peer: PeerID, timeout: number = 30000) {\n this.inner = new AwarenessWasm(peer, timeout);\n this.peer = peer;\n this.timeout = timeout;\n }\n\n apply(bytes: Uint8Array, origin = \"remote\") {\n const { updated, added } = this.inner.apply(bytes);\n this.listeners.forEach((listener) => {\n listener({ updated, added, removed: [] }, origin);\n });\n\n this.startTimerIfNotEmpty();\n }\n\n setLocalState(state: T) {\n const wasEmpty = this.inner.getState(this.peer) == null;\n this.inner.setLocalState(state);\n if (wasEmpty) {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [], added: [this.inner.peer()], removed: [] },\n \"local\",\n );\n });\n } else {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [this.inner.peer()], added: [], removed: [] },\n \"local\",\n );\n });\n }\n\n this.startTimerIfNotEmpty();\n }\n\n getLocalState(): T | undefined {\n return this.inner.getState(this.peer);\n }\n\n getAllStates(): Record<PeerID, T> {\n return this.inner.getAllStates();\n }\n\n encode(peers: PeerID[]): Uint8Array {\n return this.inner.encode(peers);\n }\n\n encodeAll(): Uint8Array {\n return this.inner.encodeAll();\n }\n\n addListener(listener: AwarenessListener) {\n this.listeners.add(listener);\n }\n\n removeListener(listener: AwarenessListener) {\n this.listeners.delete(listener);\n }\n\n peers(): PeerID[] {\n return this.inner.peers();\n }\n\n destroy() {\n clearInterval(this.timer);\n this.listeners.clear();\n }\n\n private startTimerIfNotEmpty() {\n if (this.inner.isEmpty() || this.timer != null) {\n return;\n }\n\n this.timer = setInterval(() => {\n const removed = this.inner.removeOutdated();\n if (removed.length > 0) {\n this.listeners.forEach((listener) => {\n listener({ updated: [], added: [], removed }, \"timeout\");\n });\n }\n if (this.inner.isEmpty()) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n }, this.timeout / 2) as unknown as number;\n }\n}\n","export * from \"loro-wasm\";\nimport {\n Container,\n ContainerID,\n Delta,\n Loro,\n LoroList,\n LoroMap,\n LoroText,\n LoroTree,\n OpId,\n TreeID,\n Value,\n} from \"loro-wasm\";\nexport { Awareness } from \"./awareness\";\n\nexport type Frontiers = OpId[];\n\n/**\n * Represents a path to identify the exact location of an event's target.\n * The path is composed of numbers (e.g., indices of a list container) strings\n * (e.g., keys of a map container) and TreeID (the node of a tree container),\n * indicating the absolute position of the event's source within a loro document.\n */\nexport type Path = (number | string | TreeID)[];\n\n/**\n * A batch of events that created by a single `import`/`transaction`/`checkout`.\n *\n * @prop by - How the event is triggered.\n * @prop origin - (Optional) Provides information about the origin of the event.\n * @prop diff - Contains the differential information related to the event.\n * @prop target - Identifies the container ID of the event's target.\n * @prop path - Specifies the absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\nexport interface LoroEventBatch {\n /**\n * How the event is triggered.\n *\n * - `local`: The event is triggered by a local transaction.\n * - `import`: The event is triggered by an import operation.\n * - `checkout`: The event is triggered by a checkout operation.\n */\n by: \"local\" | \"import\" | \"checkout\";\n origin?: string;\n /**\n * The container ID of the current event receiver.\n * It's undefined if the subscriber is on the root document.\n */\n currentTarget?: ContainerID;\n events: LoroEvent[];\n}\n\n/**\n * The concrete event of Loro.\n */\nexport interface LoroEvent {\n /**\n * The container ID of the event's target.\n */\n target: ContainerID;\n diff: Diff;\n /**\n * The absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\n path: Path;\n}\n\nexport type ListDiff = {\n type: \"list\";\n diff: Delta<(Value | Container)[]>[];\n};\n\nexport type TextDiff = {\n type: \"text\";\n diff: Delta<string>[];\n};\n\nexport type MapDiff = {\n type: \"map\";\n updated: Record<string, Value | Container | undefined>;\n};\n\nexport type TreeDiffItem =\n | { target: TreeID; action: \"create\"; parent: TreeID | undefined; index: number; position: string }\n | { target: TreeID; action: \"delete\" }\n | { target: TreeID; action: \"move\"; parent: TreeID | undefined; index: number; position: string };\n\nexport type TreeDiff = {\n type: \"tree\";\n diff: TreeDiffItem[];\n};\n\nexport type Diff = ListDiff | TextDiff | MapDiff | TreeDiff;\n\ninterface Listener {\n (event: LoroEventBatch): void;\n}\n\nconst CONTAINER_TYPES = [\"Map\", \"Text\", \"List\", \"Tree\"];\n\nexport function isContainerId(s: string): s is ContainerID {\n return s.startsWith(\"cid:\");\n}\n\nexport { Loro };\n\n/** Whether the value is a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * isContainer(map); // true\n * isContainer(list); // true\n * isContainer(text); // true\n * isContainer(123); // false\n * isContainer(\"123\"); // false\n * isContainer({}); // false\n */\nexport function isContainer(value: any): value is Container {\n if (typeof value !== \"object\" || value == null) {\n return false;\n }\n\n const p = Object.getPrototypeOf(value);\n if (p == null || typeof p !== \"object\" || typeof p[\"kind\"] !== \"function\") {\n return false;\n }\n\n return CONTAINER_TYPES.includes(value.kind());\n}\n\n/** Get the type of a value that may be a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * getType(map); // \"Map\"\n * getType(list); // \"List\"\n * getType(text); // \"Text\"\n * getType(123); // \"Json\"\n * getType(\"123\"); // \"Json\"\n * getType({}); // \"Json\"\n * ```\n */\nexport function getType<T>(\n value: T,\n): T extends LoroText ? \"Text\"\n : T extends LoroMap<any> ? \"Map\"\n : T extends LoroTree<any> ? \"Tree\"\n : T extends LoroList<any> ? \"List\"\n : \"Json\" {\n if (isContainer(value)) {\n return value.kind() as unknown as any;\n }\n\n return \"Json\" as any;\n}\n\ndeclare module \"loro-wasm\" {\n interface Loro {\n subscribe(listener: Listener): number;\n }\n\n interface Loro<\n T extends Record<string, Container> = Record<string, Container>,\n > {\n /**\n * Get a LoroMap by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * ```\n */\n getMap<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMap ? T[Key] : LoroMap;\n /**\n * Get a LoroList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getList<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroList ? T[Key] : LoroList;\n /**\n * Get a LoroMovableList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getMovableList<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;\n /**\n * Get a LoroTree by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const tree = doc.getTree(\"tree\");\n * ```\n */\n getTree<Key extends (keyof T) | ContainerID>(\n name: Key,\n ): T[Key] extends LoroTree ? T[Key] : LoroTree;\n getText(key: string | ContainerID): LoroText;\n }\n\n interface LoroList<T = unknown> {\n new (): LoroList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroList<T>;\n }\n\n interface LoroMovableList<T = unknown> {\n new (): LoroMovableList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMoableList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroMovableList<T>;\n /**\n * Set the value at the given position.\n *\n * It's different from `delete` + `insert` that it will replace the value at the position.\n *\n * For example, if you have a list `[1, 2, 3]`, and you call `set(1, 100)`, the list will be `[1, 100, 3]`.\n * If concurrently someone call `set(1, 200)`, the list will be `[1, 200, 3]` or `[1, 100, 3]`.\n *\n * But if you use `delete` + `insert` to simulate the set operation, they may create redundant operations\n * and the final result will be `[1, 100, 200, 3]` or `[1, 200, 100, 3]`.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.set(1, \"bar\");\n * console.log(list.value); // [100, \"bar\", true];\n * ```\n */\n set<V extends T>(pos: number, value: Exclude<V, Container>): void;\n /**\n * Set a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.setContainer(0, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [\"Hello\"];\n * ```\n */\n setContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n }\n\n interface LoroMap<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroMap<T>;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object returned is a new js object each time because it need to cross\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n getOrCreateContainer<C extends Container>(key: string, child: C): C;\n /**\n * Set the key with a container.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const text = map.setContainer(\"text\", new LoroText());\n * const list = map.setContainer(\"list\", new LoroText());\n * ```\n */\n setContainer<C extends Container, Key extends keyof T>(\n key: Key,\n child: C,\n ): NonNullableType<T[Key]> extends C ? NonNullableType<T[Key]> : C;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object/value returned is a new js object/value each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n get<Key extends keyof T>(key: Key): T[Key];\n /**\n * Set the key with the value.\n *\n * If the value of the key is exist, the old value will be updated.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * map.set(\"foo\", \"baz\");\n * ```\n */\n set<Key extends keyof T, V extends T[Key]>(\n key: Key,\n value: Exclude<V, Container>,\n ): void;\n delete(key: string): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroText {\n new (): LoroText;\n insert(pos: number, text: string): void;\n delete(pos: number, len: number): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTree<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroTree<T>;\n createNode(parent?: TreeID, index?: number ): LoroTreeNode<T>;\n move(target: TreeID, parent?: TreeID, index?: number): void;\n delete(target: TreeID): void;\n has(target: TreeID): boolean;\n getNodeByID(target: TreeID): LoroTreeNode;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTreeNode<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n /**\n * Get the associated metadata map container of a tree node.\n */\n readonly data: LoroMap<T>;\n createNode(index?: number): LoroTreeNode<T>;\n move(parent?: LoroTreeNode<T>, index?: number): void;\n parent(): LoroTreeNode<T> | undefined;\n children(): Array<LoroTreeNode<T>>;\n }\n\n interface AwarenessWasm<\n T extends Value = Value,\n > {\n getState(peer: PeerID): T | undefined;\n getTimestamp(peer: PeerID): number | undefined;\n getAllStates(): Record<PeerID, T>;\n setLocalState(value: T): void;\n removeOutdated(): PeerID[];\n }\n}\n\ntype NonNullableType<T> = Exclude<T, null | undefined>;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loro.mjs","sources":["../src/awareness.ts","../src/index.ts"],"sourcesContent":["import { AwarenessWasm, PeerID, Value } from \"loro-wasm\";\n\nexport type AwarenessListener = (\n arg: { updated: PeerID[]; added: PeerID[]; removed: PeerID[] },\n origin: \"local\" | \"timeout\" | \"remote\" | string,\n) => void;\n\n/**\n * Awareness is a structure that allows to track the ephemeral state of the peers.\n *\n * If we don't receive a state update from a peer within the timeout, we will remove their state.\n * The timeout is in milliseconds. This can be used to handle the off-line state of a peer.\n */\nexport class Awareness<T extends Value = Value> {\n inner: AwarenessWasm<T>;\n private peer: PeerID;\n private timer: number | undefined;\n private timeout: number;\n private listeners: Set<AwarenessListener> = new Set();\n constructor(peer: PeerID, timeout: number = 30000) {\n this.inner = new AwarenessWasm(peer, timeout);\n this.peer = peer;\n this.timeout = timeout;\n }\n\n apply(bytes: Uint8Array, origin = \"remote\") {\n const { updated, added } = this.inner.apply(bytes);\n this.listeners.forEach((listener) => {\n listener({ updated, added, removed: [] }, origin);\n });\n\n this.startTimerIfNotEmpty();\n }\n\n setLocalState(state: T) {\n const wasEmpty = this.inner.getState(this.peer) == null;\n this.inner.setLocalState(state);\n if (wasEmpty) {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [], added: [this.inner.peer()], removed: [] },\n \"local\",\n );\n });\n } else {\n this.listeners.forEach((listener) => {\n listener(\n { updated: [this.inner.peer()], added: [], removed: [] },\n \"local\",\n );\n });\n }\n\n this.startTimerIfNotEmpty();\n }\n\n getLocalState(): T | undefined {\n return this.inner.getState(this.peer);\n }\n\n getAllStates(): Record<PeerID, T> {\n return this.inner.getAllStates();\n }\n\n encode(peers: PeerID[]): Uint8Array {\n return this.inner.encode(peers);\n }\n\n encodeAll(): Uint8Array {\n return this.inner.encodeAll();\n }\n\n addListener(listener: AwarenessListener) {\n this.listeners.add(listener);\n }\n\n removeListener(listener: AwarenessListener) {\n this.listeners.delete(listener);\n }\n\n peers(): PeerID[] {\n return this.inner.peers();\n }\n\n destroy() {\n clearInterval(this.timer);\n this.listeners.clear();\n }\n\n private startTimerIfNotEmpty() {\n if (this.inner.isEmpty() || this.timer != null) {\n return;\n }\n\n this.timer = setInterval(() => {\n const removed = this.inner.removeOutdated();\n if (removed.length > 0) {\n this.listeners.forEach((listener) => {\n listener({ updated: [], added: [], removed }, \"timeout\");\n });\n }\n if (this.inner.isEmpty()) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n }, this.timeout / 2) as unknown as number;\n }\n}\n","export * from \"loro-wasm\";\nimport {\n Container,\n ContainerID,\n Delta,\n Loro,\n LoroList,\n LoroMap,\n LoroText,\n LoroTree,\n OpId,\n TreeID,\n Value,\n} from \"loro-wasm\";\nexport { Awareness } from \"./awareness\";\n\nexport type Frontiers = OpId[];\n\n/**\n * Represents a path to identify the exact location of an event's target.\n * The path is composed of numbers (e.g., indices of a list container) strings\n * (e.g., keys of a map container) and TreeID (the node of a tree container),\n * indicating the absolute position of the event's source within a loro document.\n */\nexport type Path = (number | string | TreeID)[];\n\n/**\n * A batch of events that created by a single `import`/`transaction`/`checkout`.\n *\n * @prop by - How the event is triggered.\n * @prop origin - (Optional) Provides information about the origin of the event.\n * @prop diff - Contains the differential information related to the event.\n * @prop target - Identifies the container ID of the event's target.\n * @prop path - Specifies the absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\nexport interface LoroEventBatch {\n /**\n * How the event is triggered.\n *\n * - `local`: The event is triggered by a local transaction.\n * - `import`: The event is triggered by an import operation.\n * - `checkout`: The event is triggered by a checkout operation.\n */\n by: \"local\" | \"import\" | \"checkout\";\n origin?: string;\n /**\n * The container ID of the current event receiver.\n * It's undefined if the subscriber is on the root document.\n */\n currentTarget?: ContainerID;\n events: LoroEvent[];\n}\n\n/**\n * The concrete event of Loro.\n */\nexport interface LoroEvent {\n /**\n * The container ID of the event's target.\n */\n target: ContainerID;\n diff: Diff;\n /**\n * The absolute path of the event's emitter, which can be an index of a list container or a key of a map container.\n */\n path: Path;\n}\n\nexport type ListDiff = {\n type: \"list\";\n diff: Delta<(Value | Container)[]>[];\n};\n\nexport type TextDiff = {\n type: \"text\";\n diff: Delta<string>[];\n};\n\nexport type MapDiff = {\n type: \"map\";\n updated: Record<string, Value | Container | undefined>;\n};\n\nexport type TreeDiffItem =\n | {\n target: TreeID;\n action: \"create\";\n parent: TreeID | undefined;\n index: number;\n position: string;\n }\n | { target: TreeID; action: \"delete\" }\n | {\n target: TreeID;\n action: \"move\";\n parent: TreeID | undefined;\n index: number;\n position: string;\n };\n\nexport type TreeDiff = {\n type: \"tree\";\n diff: TreeDiffItem[];\n};\n\nexport type Diff = ListDiff | TextDiff | MapDiff | TreeDiff;\n\ninterface Listener {\n (event: LoroEventBatch): void;\n}\n\nconst CONTAINER_TYPES = [\"Map\", \"Text\", \"List\", \"Tree\", \"MovableList\"];\n\nexport function isContainerId(s: string): s is ContainerID {\n return s.startsWith(\"cid:\");\n}\n\nexport { Loro };\n\n/** Whether the value is a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * isContainer(map); // true\n * isContainer(list); // true\n * isContainer(text); // true\n * isContainer(123); // false\n * isContainer(\"123\"); // false\n * isContainer({}); // false\n */\nexport function isContainer(value: any): value is Container {\n if (typeof value !== \"object\" || value == null) {\n return false;\n }\n\n const p = Object.getPrototypeOf(value);\n if (p == null || typeof p !== \"object\" || typeof p[\"kind\"] !== \"function\") {\n return false;\n }\n\n return CONTAINER_TYPES.includes(value.kind());\n}\n\n/** Get the type of a value that may be a container.\n *\n * # Example\n *\n * ```ts\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * const list = doc.getList(\"list\");\n * const text = doc.getText(\"text\");\n * getType(map); // \"Map\"\n * getType(list); // \"List\"\n * getType(text); // \"Text\"\n * getType(123); // \"Json\"\n * getType(\"123\"); // \"Json\"\n * getType({}); // \"Json\"\n * ```\n */\nexport function getType<T>(\n value: T,\n): T extends LoroText\n ? \"Text\"\n : T extends LoroMap<any>\n ? \"Map\"\n : T extends LoroTree<any>\n ? \"Tree\"\n : T extends LoroList<any>\n ? \"List\"\n : \"Json\" {\n if (isContainer(value)) {\n return value.kind() as unknown as any;\n }\n\n return \"Json\" as any;\n}\n\ndeclare module \"loro-wasm\" {\n interface Loro {\n subscribe(listener: Listener): number;\n }\n\n interface Loro<\n T extends Record<string, Container> = Record<string, Container>,\n > {\n /**\n * Get a LoroMap by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * ```\n */\n getMap<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMap ? T[Key] : LoroMap;\n /**\n * Get a LoroList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getList<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroList ? T[Key] : LoroList;\n /**\n * Get a LoroMovableList by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * ```\n */\n getMovableList<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;\n /**\n * Get a LoroTree by container id\n *\n * The object returned is a new js object each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const tree = doc.getTree(\"tree\");\n * ```\n */\n getTree<Key extends keyof T | ContainerID>(\n name: Key,\n ): T[Key] extends LoroTree ? T[Key] : LoroTree;\n getText(key: string | ContainerID): LoroText;\n }\n\n interface LoroList<T = unknown> {\n new (): LoroList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroList<T>;\n }\n\n interface LoroMovableList<T = unknown> {\n new (): LoroMovableList<T>;\n /**\n * Get elements of the list. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * @example\n * ```ts\n * import { Loro, LoroText } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.insertContainer(3, new LoroText());\n * console.log(list.value); // [100, \"foo\", true, LoroText];\n * ```\n */\n toArray(): T[];\n /**\n * Insert a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.insertContainer(1, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [100, \"Hello\"];\n * ```\n */\n insertContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n /**\n * Get the value at the index. If the value is a container, the corresponding handler will be returned.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMoableList(\"list\");\n * list.insert(0, 100);\n * console.log(list.get(0)); // 100\n * console.log(list.get(1)); // undefined\n * ```\n */\n get(index: number): T;\n /**\n * Insert a value at index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * console.log(list.value); // [100, \"foo\", true];\n * ```\n */\n insert<V extends T>(pos: number, value: Exclude<V, Container>): void;\n delete(pos: number, len: number): void;\n push<V extends T>(value: Exclude<V, Container>): void;\n subscribe(listener: Listener): number;\n getAttached(): undefined | LoroMovableList<T>;\n /**\n * Set the value at the given position.\n *\n * It's different from `delete` + `insert` that it will replace the value at the position.\n *\n * For example, if you have a list `[1, 2, 3]`, and you call `set(1, 100)`, the list will be `[1, 100, 3]`.\n * If concurrently someone call `set(1, 200)`, the list will be `[1, 200, 3]` or `[1, 100, 3]`.\n *\n * But if you use `delete` + `insert` to simulate the set operation, they may create redundant operations\n * and the final result will be `[1, 100, 200, 3]` or `[1, 200, 100, 3]`.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getList(\"list\");\n * list.insert(0, 100);\n * list.insert(1, \"foo\");\n * list.insert(2, true);\n * list.set(1, \"bar\");\n * console.log(list.value); // [100, \"bar\", true];\n * ```\n */\n set<V extends T>(pos: number, value: Exclude<V, Container>): void;\n /**\n * Set a container at the index.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const list = doc.getMovableList(\"list\");\n * list.insert(0, 100);\n * const text = list.setContainer(0, new LoroText());\n * text.insert(0, \"Hello\");\n * console.log(list.toJSON()); // [\"Hello\"];\n * ```\n */\n setContainer<C extends Container>(\n pos: number,\n child: C,\n ): T extends C ? T : C;\n }\n\n interface LoroMap<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroMap<T>;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object returned is a new js object each time because it need to cross\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n getOrCreateContainer<C extends Container>(key: string, child: C): C;\n /**\n * Set the key with a container.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const text = map.setContainer(\"text\", new LoroText());\n * const list = map.setContainer(\"list\", new LoroText());\n * ```\n */\n setContainer<C extends Container, Key extends keyof T>(\n key: Key,\n child: C,\n ): NonNullableType<T[Key]> extends C ? NonNullableType<T[Key]> : C;\n /**\n * Get the value of the key. If the value is a child container, the corresponding\n * `Container` will be returned.\n *\n * The object/value returned is a new js object/value each time because it need to cross\n * the WASM boundary.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * const bar = map.get(\"foo\");\n * ```\n */\n get<Key extends keyof T>(key: Key): T[Key];\n /**\n * Set the key with the value.\n *\n * If the value of the key is exist, the old value will be updated.\n *\n * @example\n * ```ts\n * import { Loro } from \"loro-crdt\";\n *\n * const doc = new Loro();\n * const map = doc.getMap(\"map\");\n * map.set(\"foo\", \"bar\");\n * map.set(\"foo\", \"baz\");\n * ```\n */\n set<Key extends keyof T, V extends T[Key]>(\n key: Key,\n value: Exclude<V, Container>,\n ): void;\n delete(key: string): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroText {\n new (): LoroText;\n insert(pos: number, text: string): void;\n delete(pos: number, len: number): void;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTree<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n new (): LoroTree<T>;\n createNode(parent?: TreeID, index?: number): LoroTreeNode<T>;\n move(target: TreeID, parent?: TreeID, index?: number): void;\n delete(target: TreeID): void;\n has(target: TreeID): boolean;\n getNodeByID(target: TreeID): LoroTreeNode;\n subscribe(listener: Listener): number;\n }\n\n interface LoroTreeNode<\n T extends Record<string, unknown> = Record<string, unknown>,\n > {\n /**\n * Get the associated metadata map container of a tree node.\n */\n readonly data: LoroMap<T>;\n createNode(index?: number): LoroTreeNode<T>;\n move(parent?: LoroTreeNode<T>, index?: number): void;\n parent(): LoroTreeNode<T> | undefined;\n children(): Array<LoroTreeNode<T>>;\n }\n\n interface AwarenessWasm<T extends Value = Value> {\n getState(peer: PeerID): T | undefined;\n getTimestamp(peer: PeerID): number | undefined;\n getAllStates(): Record<PeerID, T>;\n setLocalState(value: T): void;\n removeOutdated(): PeerID[];\n }\n}\n\ntype NonNullableType<T> = Exclude<T, null | undefined>;\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loro-crdt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Loro CRDTs is a high-performance CRDT framework that makes your app state synchronized, collaborative and maintainable effortlessly.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"crdt",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"homepage": "https://loro.dev",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"loro-wasm": "0.
|
|
20
|
+
"loro-wasm": "0.16.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@vitest/ui": "^1.0.4",
|
|
26
26
|
"esbuild": "^0.18.20",
|
|
27
27
|
"eslint": "^8.46.0",
|
|
28
|
+
"loro-crdt-old": "npm:loro-crdt@0.15.4",
|
|
28
29
|
"prettier": "^3.0.0",
|
|
29
30
|
"rollup": "^3.20.1",
|
|
30
31
|
"rollup-plugin-dts": "^5.3.0",
|
package/src/index.ts
CHANGED
|
@@ -82,9 +82,21 @@ export type MapDiff = {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
export type TreeDiffItem =
|
|
85
|
-
| {
|
|
85
|
+
| {
|
|
86
|
+
target: TreeID;
|
|
87
|
+
action: "create";
|
|
88
|
+
parent: TreeID | undefined;
|
|
89
|
+
index: number;
|
|
90
|
+
position: string;
|
|
91
|
+
}
|
|
86
92
|
| { target: TreeID; action: "delete" }
|
|
87
|
-
| {
|
|
93
|
+
| {
|
|
94
|
+
target: TreeID;
|
|
95
|
+
action: "move";
|
|
96
|
+
parent: TreeID | undefined;
|
|
97
|
+
index: number;
|
|
98
|
+
position: string;
|
|
99
|
+
};
|
|
88
100
|
|
|
89
101
|
export type TreeDiff = {
|
|
90
102
|
type: "tree";
|
|
@@ -97,7 +109,7 @@ interface Listener {
|
|
|
97
109
|
(event: LoroEventBatch): void;
|
|
98
110
|
}
|
|
99
111
|
|
|
100
|
-
const CONTAINER_TYPES = ["Map", "Text", "List", "Tree"];
|
|
112
|
+
const CONTAINER_TYPES = ["Map", "Text", "List", "Tree", "MovableList"];
|
|
101
113
|
|
|
102
114
|
export function isContainerId(s: string): s is ContainerID {
|
|
103
115
|
return s.startsWith("cid:");
|
|
@@ -153,11 +165,15 @@ export function isContainer(value: any): value is Container {
|
|
|
153
165
|
*/
|
|
154
166
|
export function getType<T>(
|
|
155
167
|
value: T,
|
|
156
|
-
): T extends LoroText
|
|
157
|
-
|
|
158
|
-
: T extends
|
|
159
|
-
|
|
160
|
-
|
|
168
|
+
): T extends LoroText
|
|
169
|
+
? "Text"
|
|
170
|
+
: T extends LoroMap<any>
|
|
171
|
+
? "Map"
|
|
172
|
+
: T extends LoroTree<any>
|
|
173
|
+
? "Tree"
|
|
174
|
+
: T extends LoroList<any>
|
|
175
|
+
? "List"
|
|
176
|
+
: "Json" {
|
|
161
177
|
if (isContainer(value)) {
|
|
162
178
|
return value.kind() as unknown as any;
|
|
163
179
|
}
|
|
@@ -187,7 +203,7 @@ declare module "loro-wasm" {
|
|
|
187
203
|
* const map = doc.getMap("map");
|
|
188
204
|
* ```
|
|
189
205
|
*/
|
|
190
|
-
getMap<Key extends
|
|
206
|
+
getMap<Key extends keyof T | ContainerID>(
|
|
191
207
|
name: Key,
|
|
192
208
|
): T[Key] extends LoroMap ? T[Key] : LoroMap;
|
|
193
209
|
/**
|
|
@@ -204,7 +220,7 @@ declare module "loro-wasm" {
|
|
|
204
220
|
* const list = doc.getList("list");
|
|
205
221
|
* ```
|
|
206
222
|
*/
|
|
207
|
-
getList<Key extends
|
|
223
|
+
getList<Key extends keyof T | ContainerID>(
|
|
208
224
|
name: Key,
|
|
209
225
|
): T[Key] extends LoroList ? T[Key] : LoroList;
|
|
210
226
|
/**
|
|
@@ -221,7 +237,7 @@ declare module "loro-wasm" {
|
|
|
221
237
|
* const list = doc.getList("list");
|
|
222
238
|
* ```
|
|
223
239
|
*/
|
|
224
|
-
getMovableList<Key extends
|
|
240
|
+
getMovableList<Key extends keyof T | ContainerID>(
|
|
225
241
|
name: Key,
|
|
226
242
|
): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;
|
|
227
243
|
/**
|
|
@@ -238,7 +254,7 @@ declare module "loro-wasm" {
|
|
|
238
254
|
* const tree = doc.getTree("tree");
|
|
239
255
|
* ```
|
|
240
256
|
*/
|
|
241
|
-
getTree<Key extends
|
|
257
|
+
getTree<Key extends keyof T | ContainerID>(
|
|
242
258
|
name: Key,
|
|
243
259
|
): T[Key] extends LoroTree ? T[Key] : LoroTree;
|
|
244
260
|
getText(key: string | ContainerID): LoroText;
|
|
@@ -531,7 +547,7 @@ declare module "loro-wasm" {
|
|
|
531
547
|
T extends Record<string, unknown> = Record<string, unknown>,
|
|
532
548
|
> {
|
|
533
549
|
new (): LoroTree<T>;
|
|
534
|
-
createNode(parent?: TreeID, index?: number
|
|
550
|
+
createNode(parent?: TreeID, index?: number): LoroTreeNode<T>;
|
|
535
551
|
move(target: TreeID, parent?: TreeID, index?: number): void;
|
|
536
552
|
delete(target: TreeID): void;
|
|
537
553
|
has(target: TreeID): boolean;
|
|
@@ -552,9 +568,7 @@ declare module "loro-wasm" {
|
|
|
552
568
|
children(): Array<LoroTreeNode<T>>;
|
|
553
569
|
}
|
|
554
570
|
|
|
555
|
-
interface AwarenessWasm<
|
|
556
|
-
T extends Value = Value,
|
|
557
|
-
> {
|
|
571
|
+
interface AwarenessWasm<T extends Value = Value> {
|
|
558
572
|
getState(peer: PeerID): T | undefined;
|
|
559
573
|
getTimestamp(peer: PeerID): number | undefined;
|
|
560
574
|
getAllStates(): Record<PeerID, T>;
|