loro-crdt 0.14.6 → 0.15.1
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 +69 -0
- package/dist/loro.d.ts +137 -8
- package/dist/loro.js.map +1 -1
- package/dist/loro.mjs.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +149 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 04c6290: Bug fixes and improvements.
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- Impl a few unimplemented! for movable tree (#335)
|
|
12
|
+
- Refine ts type; reject invalid operations (#334)
|
|
13
|
+
- Get cursor err on text and movable list (#337)
|
|
14
|
+
- Missing MovableList in all container type (#343)
|
|
15
|
+
- Upgrade generic-btree to allow large btree (#344)
|
|
16
|
+
|
|
17
|
+
#### 📚 Documentation
|
|
18
|
+
|
|
19
|
+
- Add warn(missing_docs) to loro and loro-wasm (#339)
|
|
20
|
+
- Minor fix on set_change_merge_interval api (#341)
|
|
21
|
+
|
|
22
|
+
#### ⚙️ Miscellaneous Tasks
|
|
23
|
+
|
|
24
|
+
- Skip the checking if not debug_assertions (#340)
|
|
25
|
+
|
|
26
|
+
<!-- generated by git-cliff -->
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [04c6290]
|
|
29
|
+
- loro-wasm@0.15.1
|
|
30
|
+
|
|
31
|
+
## 0.15.0
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- 35b9b6e: Movable List (#293)
|
|
36
|
+
|
|
37
|
+
Loro's List supports insert and delete operations but lacks built-in methods for `set` and `move`. To simulate set and move, developers might combine delete and insert. However, this approach can lead to issues during concurrent operations on the same element, often resulting in duplicate entries upon merging.
|
|
38
|
+
|
|
39
|
+
For instance, consider a list [0, 1, 2]. If user A moves the element '0' to position 1, while user B moves it to position 2, the ideal merged outcome should be either [1, 0, 2] or [1, 2, 0]. However, using the delete-insert method to simulate a move results in [1, 0, 2, 0], as both users delete '0' from its original position and insert it independently at new positions.
|
|
40
|
+
|
|
41
|
+
To address this, we introduce a MovableList container. This new container type directly supports move and set operations, aligning more closely with user expectations and preventing the issues associated with simulated moves.
|
|
42
|
+
|
|
43
|
+
## Example
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { Loro } from "loro-crdt";
|
|
47
|
+
import { expect } from "vitest";
|
|
48
|
+
|
|
49
|
+
const doc = new Loro();
|
|
50
|
+
const list = doc.getMovableList("list");
|
|
51
|
+
list.push("a");
|
|
52
|
+
list.push("b");
|
|
53
|
+
list.push("c");
|
|
54
|
+
expect(list.toArray()).toEqual(["a", "b", "c"]);
|
|
55
|
+
list.set(2, "d");
|
|
56
|
+
list.move(0, 1);
|
|
57
|
+
const doc2 = new Loro();
|
|
58
|
+
const list2 = doc2.getMovableList("list");
|
|
59
|
+
expect(list2.length).toBe(0);
|
|
60
|
+
doc2.import(doc.exportFrom());
|
|
61
|
+
expect(list2.length).toBe(3);
|
|
62
|
+
expect(list2.get(0)).toBe("b");
|
|
63
|
+
expect(list2.get(1)).toBe("a");
|
|
64
|
+
expect(list2.get(2)).toBe("d");
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Patch Changes
|
|
68
|
+
|
|
69
|
+
- Updated dependencies [35b9b6e]
|
|
70
|
+
- loro-wasm@0.15.0
|
|
71
|
+
|
|
3
72
|
## 0.14.6
|
|
4
73
|
|
|
5
74
|
### Patch Changes
|
package/dist/loro.d.ts
CHANGED
|
@@ -185,6 +185,21 @@ declare module "loro-wasm" {
|
|
|
185
185
|
* ```
|
|
186
186
|
*/
|
|
187
187
|
getList<Key extends (keyof T) | ContainerID>(name: Key): T[Key] extends LoroList ? T[Key] : LoroList;
|
|
188
|
+
/**
|
|
189
|
+
* Get a LoroMovableList by container id
|
|
190
|
+
*
|
|
191
|
+
* The object returned is a new js object each time because it need to cross
|
|
192
|
+
* the WASM boundary.
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* import { Loro } from "loro-crdt";
|
|
197
|
+
*
|
|
198
|
+
* const doc = new Loro();
|
|
199
|
+
* const list = doc.getList("list");
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
getMovableList<Key extends (keyof T) | ContainerID>(name: Key): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;
|
|
188
203
|
/**
|
|
189
204
|
* Get a LoroTree by container id
|
|
190
205
|
*
|
|
@@ -227,14 +242,14 @@ declare module "loro-wasm" {
|
|
|
227
242
|
*
|
|
228
243
|
* @example
|
|
229
244
|
* ```ts
|
|
230
|
-
* import { Loro } from "loro-crdt";
|
|
245
|
+
* import { Loro, LoroText } from "loro-crdt";
|
|
231
246
|
*
|
|
232
247
|
* const doc = new Loro();
|
|
233
248
|
* const list = doc.getList("list");
|
|
234
249
|
* list.insert(0, 100);
|
|
235
250
|
* const text = list.insertContainer(1, new LoroText());
|
|
236
251
|
* text.insert(0, "Hello");
|
|
237
|
-
* console.log(list.
|
|
252
|
+
* console.log(list.toJSON()); // [100, "Hello"];
|
|
238
253
|
* ```
|
|
239
254
|
*/
|
|
240
255
|
insertContainer<C extends Container>(pos: number, child: C): T extends C ? T : C;
|
|
@@ -268,11 +283,125 @@ declare module "loro-wasm" {
|
|
|
268
283
|
* console.log(list.value); // [100, "foo", true];
|
|
269
284
|
* ```
|
|
270
285
|
*/
|
|
271
|
-
insert(pos: number, value: Exclude<
|
|
286
|
+
insert<V extends T>(pos: number, value: Exclude<V, Container>): void;
|
|
272
287
|
delete(pos: number, len: number): void;
|
|
273
|
-
|
|
288
|
+
push<V extends T>(value: Exclude<V, Container>): void;
|
|
289
|
+
subscribe(listener: Listener): number;
|
|
274
290
|
getAttached(): undefined | LoroList<T>;
|
|
275
291
|
}
|
|
292
|
+
interface LoroMovableList<T = unknown> {
|
|
293
|
+
new (): LoroMovableList<T>;
|
|
294
|
+
/**
|
|
295
|
+
* Get elements of the list. If the value is a child container, the corresponding
|
|
296
|
+
* `Container` will be returned.
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```ts
|
|
300
|
+
* import { Loro, LoroText } from "loro-crdt";
|
|
301
|
+
*
|
|
302
|
+
* const doc = new Loro();
|
|
303
|
+
* const list = doc.getMovableList("list");
|
|
304
|
+
* list.insert(0, 100);
|
|
305
|
+
* list.insert(1, "foo");
|
|
306
|
+
* list.insert(2, true);
|
|
307
|
+
* list.insertContainer(3, new LoroText());
|
|
308
|
+
* console.log(list.value); // [100, "foo", true, LoroText];
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
toArray(): T[];
|
|
312
|
+
/**
|
|
313
|
+
* Insert a container at the index.
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* import { Loro } from "loro-crdt";
|
|
318
|
+
*
|
|
319
|
+
* const doc = new Loro();
|
|
320
|
+
* const list = doc.getMovableList("list");
|
|
321
|
+
* list.insert(0, 100);
|
|
322
|
+
* const text = list.insertContainer(1, new LoroText());
|
|
323
|
+
* text.insert(0, "Hello");
|
|
324
|
+
* console.log(list.toJSON()); // [100, "Hello"];
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
insertContainer<C extends Container>(pos: number, child: C): T extends C ? T : C;
|
|
328
|
+
/**
|
|
329
|
+
* Get the value at the index. If the value is a container, the corresponding handler will be returned.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```ts
|
|
333
|
+
* import { Loro } from "loro-crdt";
|
|
334
|
+
*
|
|
335
|
+
* const doc = new Loro();
|
|
336
|
+
* const list = doc.getMoableList("list");
|
|
337
|
+
* list.insert(0, 100);
|
|
338
|
+
* console.log(list.get(0)); // 100
|
|
339
|
+
* console.log(list.get(1)); // undefined
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
get(index: number): T;
|
|
343
|
+
/**
|
|
344
|
+
* Insert a value at index.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* import { Loro } from "loro-crdt";
|
|
349
|
+
*
|
|
350
|
+
* const doc = new Loro();
|
|
351
|
+
* const list = doc.getMovableList("list");
|
|
352
|
+
* list.insert(0, 100);
|
|
353
|
+
* list.insert(1, "foo");
|
|
354
|
+
* list.insert(2, true);
|
|
355
|
+
* console.log(list.value); // [100, "foo", true];
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
insert<V extends T>(pos: number, value: Exclude<V, Container>): void;
|
|
359
|
+
delete(pos: number, len: number): void;
|
|
360
|
+
push<V extends T>(value: Exclude<V, Container>): void;
|
|
361
|
+
subscribe(listener: Listener): number;
|
|
362
|
+
getAttached(): undefined | LoroMovableList<T>;
|
|
363
|
+
/**
|
|
364
|
+
* Set the value at the given position.
|
|
365
|
+
*
|
|
366
|
+
* It's different from `delete` + `insert` that it will replace the value at the position.
|
|
367
|
+
*
|
|
368
|
+
* For example, if you have a list `[1, 2, 3]`, and you call `set(1, 100)`, the list will be `[1, 100, 3]`.
|
|
369
|
+
* If concurrently someone call `set(1, 200)`, the list will be `[1, 200, 3]` or `[1, 100, 3]`.
|
|
370
|
+
*
|
|
371
|
+
* But if you use `delete` + `insert` to simulate the set operation, they may create redundant operations
|
|
372
|
+
* and the final result will be `[1, 100, 200, 3]` or `[1, 200, 100, 3]`.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```ts
|
|
376
|
+
* import { Loro } from "loro-crdt";
|
|
377
|
+
*
|
|
378
|
+
* const doc = new Loro();
|
|
379
|
+
* const list = doc.getList("list");
|
|
380
|
+
* list.insert(0, 100);
|
|
381
|
+
* list.insert(1, "foo");
|
|
382
|
+
* list.insert(2, true);
|
|
383
|
+
* list.set(1, "bar");
|
|
384
|
+
* console.log(list.value); // [100, "bar", true];
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
set<V extends T>(pos: number, value: Exclude<V, Container>): void;
|
|
388
|
+
/**
|
|
389
|
+
* Set a container at the index.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* import { Loro } from "loro-crdt";
|
|
394
|
+
*
|
|
395
|
+
* const doc = new Loro();
|
|
396
|
+
* const list = doc.getMovableList("list");
|
|
397
|
+
* list.insert(0, 100);
|
|
398
|
+
* const text = list.setContainer(0, new LoroText());
|
|
399
|
+
* text.insert(0, "Hello");
|
|
400
|
+
* console.log(list.toJSON()); // ["Hello"];
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
setContainer<C extends Container>(pos: number, child: C): T extends C ? T : C;
|
|
404
|
+
}
|
|
276
405
|
interface LoroMap<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
277
406
|
new (): LoroMap<T>;
|
|
278
407
|
/**
|
|
@@ -340,15 +469,15 @@ declare module "loro-wasm" {
|
|
|
340
469
|
* map.set("foo", "baz");
|
|
341
470
|
* ```
|
|
342
471
|
*/
|
|
343
|
-
set<Key extends keyof T>(key: Key, value: Exclude<
|
|
472
|
+
set<Key extends keyof T, V extends T[Key]>(key: Key, value: Exclude<V, Container>): void;
|
|
344
473
|
delete(key: string): void;
|
|
345
|
-
subscribe(
|
|
474
|
+
subscribe(listener: Listener): number;
|
|
346
475
|
}
|
|
347
476
|
interface LoroText {
|
|
348
477
|
new (): LoroText;
|
|
349
478
|
insert(pos: number, text: string): void;
|
|
350
479
|
delete(pos: number, len: number): void;
|
|
351
|
-
subscribe(
|
|
480
|
+
subscribe(listener: Listener): number;
|
|
352
481
|
}
|
|
353
482
|
interface LoroTree<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
354
483
|
new (): LoroTree<T>;
|
|
@@ -357,7 +486,7 @@ declare module "loro-wasm" {
|
|
|
357
486
|
delete(target: TreeID): void;
|
|
358
487
|
has(target: TreeID): boolean;
|
|
359
488
|
getNodeByID(target: TreeID): LoroTreeNode;
|
|
360
|
-
subscribe(
|
|
489
|
+
subscribe(listener: Listener): number;
|
|
361
490
|
}
|
|
362
491
|
interface LoroTreeNode<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
363
492
|
/**
|
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 }\n | { target: TreeID; action: \"delete\" }\n | { target: TreeID; action: \"move\"; parent: TreeID | undefined };\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 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 } 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.getDeepValue()); // [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(pos: number, value: Exclude<T, Container>): void;\n delete(pos: number, len: number): void;\n subscribe(txn: Loro, listener: Listener): number;\n getAttached(): undefined | LoroList<T>;\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>(key: Key, value: Exclude<T[Key], Container>): void;\n delete(key: string): void;\n subscribe(txn: Loro, 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(txn: Loro, 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 | undefined): LoroTreeNode<T>;\n move(target: TreeID, parent: TreeID | undefined): void;\n delete(target: TreeID): void;\n has(target: TreeID): boolean;\n getNodeByID(target: TreeID): LoroTreeNode;\n subscribe(txn: Loro, 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(): LoroTreeNode<T>;\n setAsRoot(): void;\n moveTo(parent: LoroTreeNode<T>): 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":"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"}
|
|
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 }\n | { target: TreeID; action: \"delete\" }\n | { target: TreeID; action: \"move\"; parent: TreeID | undefined };\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 | undefined): LoroTreeNode<T>;\n move(target: TreeID, parent: TreeID | undefined): 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(): LoroTreeNode<T>;\n setAsRoot(): void;\n moveTo(parent: LoroTreeNode<T>): 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":"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"}
|
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 }\n | { target: TreeID; action: \"delete\" }\n | { target: TreeID; action: \"move\"; parent: TreeID | undefined };\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 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 } 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.getDeepValue()); // [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(pos: number, value: Exclude<T, Container>): void;\n delete(pos: number, len: number): void;\n subscribe(txn: Loro, listener: Listener): number;\n getAttached(): undefined | LoroList<T>;\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>(key: Key, value: Exclude<T[Key], Container>): void;\n delete(key: string): void;\n subscribe(txn: Loro, 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(txn: Loro, 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 | undefined): LoroTreeNode<T>;\n move(target: TreeID, parent: TreeID | undefined): void;\n delete(target: TreeID): void;\n has(target: TreeID): boolean;\n getNodeByID(target: TreeID): LoroTreeNode;\n subscribe(txn: Loro, 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(): LoroTreeNode<T>;\n setAsRoot(): void;\n moveTo(parent: LoroTreeNode<T>): 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 | { target: TreeID; action: \"create\"; parent: TreeID | undefined }\n | { target: TreeID; action: \"delete\" }\n | { target: TreeID; action: \"move\"; parent: TreeID | undefined };\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 | undefined): LoroTreeNode<T>;\n move(target: TreeID, parent: TreeID | undefined): 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(): LoroTreeNode<T>;\n setAsRoot(): void;\n moveTo(parent: LoroTreeNode<T>): 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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loro-crdt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.1",
|
|
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.15.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
package/src/index.ts
CHANGED
|
@@ -207,6 +207,23 @@ declare module "loro-wasm" {
|
|
|
207
207
|
getList<Key extends (keyof T) | ContainerID>(
|
|
208
208
|
name: Key,
|
|
209
209
|
): T[Key] extends LoroList ? T[Key] : LoroList;
|
|
210
|
+
/**
|
|
211
|
+
* Get a LoroMovableList by container id
|
|
212
|
+
*
|
|
213
|
+
* The object returned is a new js object each time because it need to cross
|
|
214
|
+
* the WASM boundary.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* import { Loro } from "loro-crdt";
|
|
219
|
+
*
|
|
220
|
+
* const doc = new Loro();
|
|
221
|
+
* const list = doc.getList("list");
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
getMovableList<Key extends (keyof T) | ContainerID>(
|
|
225
|
+
name: Key,
|
|
226
|
+
): T[Key] extends LoroMovableList ? T[Key] : LoroMovableList;
|
|
210
227
|
/**
|
|
211
228
|
* Get a LoroTree by container id
|
|
212
229
|
*
|
|
@@ -252,14 +269,14 @@ declare module "loro-wasm" {
|
|
|
252
269
|
*
|
|
253
270
|
* @example
|
|
254
271
|
* ```ts
|
|
255
|
-
* import { Loro } from "loro-crdt";
|
|
272
|
+
* import { Loro, LoroText } from "loro-crdt";
|
|
256
273
|
*
|
|
257
274
|
* const doc = new Loro();
|
|
258
275
|
* const list = doc.getList("list");
|
|
259
276
|
* list.insert(0, 100);
|
|
260
277
|
* const text = list.insertContainer(1, new LoroText());
|
|
261
278
|
* text.insert(0, "Hello");
|
|
262
|
-
* console.log(list.
|
|
279
|
+
* console.log(list.toJSON()); // [100, "Hello"];
|
|
263
280
|
* ```
|
|
264
281
|
*/
|
|
265
282
|
insertContainer<C extends Container>(
|
|
@@ -296,12 +313,133 @@ declare module "loro-wasm" {
|
|
|
296
313
|
* console.log(list.value); // [100, "foo", true];
|
|
297
314
|
* ```
|
|
298
315
|
*/
|
|
299
|
-
insert(pos: number, value: Exclude<
|
|
316
|
+
insert<V extends T>(pos: number, value: Exclude<V, Container>): void;
|
|
300
317
|
delete(pos: number, len: number): void;
|
|
301
|
-
|
|
318
|
+
push<V extends T>(value: Exclude<V, Container>): void;
|
|
319
|
+
subscribe(listener: Listener): number;
|
|
302
320
|
getAttached(): undefined | LoroList<T>;
|
|
303
321
|
}
|
|
304
322
|
|
|
323
|
+
interface LoroMovableList<T = unknown> {
|
|
324
|
+
new (): LoroMovableList<T>;
|
|
325
|
+
/**
|
|
326
|
+
* Get elements of the list. If the value is a child container, the corresponding
|
|
327
|
+
* `Container` will be returned.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* import { Loro, LoroText } from "loro-crdt";
|
|
332
|
+
*
|
|
333
|
+
* const doc = new Loro();
|
|
334
|
+
* const list = doc.getMovableList("list");
|
|
335
|
+
* list.insert(0, 100);
|
|
336
|
+
* list.insert(1, "foo");
|
|
337
|
+
* list.insert(2, true);
|
|
338
|
+
* list.insertContainer(3, new LoroText());
|
|
339
|
+
* console.log(list.value); // [100, "foo", true, LoroText];
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
toArray(): T[];
|
|
343
|
+
/**
|
|
344
|
+
* Insert a container at the index.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```ts
|
|
348
|
+
* import { Loro } from "loro-crdt";
|
|
349
|
+
*
|
|
350
|
+
* const doc = new Loro();
|
|
351
|
+
* const list = doc.getMovableList("list");
|
|
352
|
+
* list.insert(0, 100);
|
|
353
|
+
* const text = list.insertContainer(1, new LoroText());
|
|
354
|
+
* text.insert(0, "Hello");
|
|
355
|
+
* console.log(list.toJSON()); // [100, "Hello"];
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
insertContainer<C extends Container>(
|
|
359
|
+
pos: number,
|
|
360
|
+
child: C,
|
|
361
|
+
): T extends C ? T : C;
|
|
362
|
+
/**
|
|
363
|
+
* Get the value at the index. If the value is a container, the corresponding handler will be returned.
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* import { Loro } from "loro-crdt";
|
|
368
|
+
*
|
|
369
|
+
* const doc = new Loro();
|
|
370
|
+
* const list = doc.getMoableList("list");
|
|
371
|
+
* list.insert(0, 100);
|
|
372
|
+
* console.log(list.get(0)); // 100
|
|
373
|
+
* console.log(list.get(1)); // undefined
|
|
374
|
+
* ```
|
|
375
|
+
*/
|
|
376
|
+
get(index: number): T;
|
|
377
|
+
/**
|
|
378
|
+
* Insert a value at index.
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```ts
|
|
382
|
+
* import { Loro } from "loro-crdt";
|
|
383
|
+
*
|
|
384
|
+
* const doc = new Loro();
|
|
385
|
+
* const list = doc.getMovableList("list");
|
|
386
|
+
* list.insert(0, 100);
|
|
387
|
+
* list.insert(1, "foo");
|
|
388
|
+
* list.insert(2, true);
|
|
389
|
+
* console.log(list.value); // [100, "foo", true];
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
insert<V extends T>(pos: number, value: Exclude<V, Container>): void;
|
|
393
|
+
delete(pos: number, len: number): void;
|
|
394
|
+
push<V extends T>(value: Exclude<V, Container>): void;
|
|
395
|
+
subscribe(listener: Listener): number;
|
|
396
|
+
getAttached(): undefined | LoroMovableList<T>;
|
|
397
|
+
/**
|
|
398
|
+
* Set the value at the given position.
|
|
399
|
+
*
|
|
400
|
+
* It's different from `delete` + `insert` that it will replace the value at the position.
|
|
401
|
+
*
|
|
402
|
+
* For example, if you have a list `[1, 2, 3]`, and you call `set(1, 100)`, the list will be `[1, 100, 3]`.
|
|
403
|
+
* If concurrently someone call `set(1, 200)`, the list will be `[1, 200, 3]` or `[1, 100, 3]`.
|
|
404
|
+
*
|
|
405
|
+
* But if you use `delete` + `insert` to simulate the set operation, they may create redundant operations
|
|
406
|
+
* and the final result will be `[1, 100, 200, 3]` or `[1, 200, 100, 3]`.
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* ```ts
|
|
410
|
+
* import { Loro } from "loro-crdt";
|
|
411
|
+
*
|
|
412
|
+
* const doc = new Loro();
|
|
413
|
+
* const list = doc.getList("list");
|
|
414
|
+
* list.insert(0, 100);
|
|
415
|
+
* list.insert(1, "foo");
|
|
416
|
+
* list.insert(2, true);
|
|
417
|
+
* list.set(1, "bar");
|
|
418
|
+
* console.log(list.value); // [100, "bar", true];
|
|
419
|
+
* ```
|
|
420
|
+
*/
|
|
421
|
+
set<V extends T>(pos: number, value: Exclude<V, Container>): void;
|
|
422
|
+
/**
|
|
423
|
+
* Set a container at the index.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* import { Loro } from "loro-crdt";
|
|
428
|
+
*
|
|
429
|
+
* const doc = new Loro();
|
|
430
|
+
* const list = doc.getMovableList("list");
|
|
431
|
+
* list.insert(0, 100);
|
|
432
|
+
* const text = list.setContainer(0, new LoroText());
|
|
433
|
+
* text.insert(0, "Hello");
|
|
434
|
+
* console.log(list.toJSON()); // ["Hello"];
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
setContainer<C extends Container>(
|
|
438
|
+
pos: number,
|
|
439
|
+
child: C,
|
|
440
|
+
): T extends C ? T : C;
|
|
441
|
+
}
|
|
442
|
+
|
|
305
443
|
interface LoroMap<
|
|
306
444
|
T extends Record<string, unknown> = Record<string, unknown>,
|
|
307
445
|
> {
|
|
@@ -374,16 +512,19 @@ declare module "loro-wasm" {
|
|
|
374
512
|
* map.set("foo", "baz");
|
|
375
513
|
* ```
|
|
376
514
|
*/
|
|
377
|
-
set<Key extends keyof T
|
|
515
|
+
set<Key extends keyof T, V extends T[Key]>(
|
|
516
|
+
key: Key,
|
|
517
|
+
value: Exclude<V, Container>,
|
|
518
|
+
): void;
|
|
378
519
|
delete(key: string): void;
|
|
379
|
-
subscribe(
|
|
520
|
+
subscribe(listener: Listener): number;
|
|
380
521
|
}
|
|
381
522
|
|
|
382
523
|
interface LoroText {
|
|
383
524
|
new (): LoroText;
|
|
384
525
|
insert(pos: number, text: string): void;
|
|
385
526
|
delete(pos: number, len: number): void;
|
|
386
|
-
subscribe(
|
|
527
|
+
subscribe(listener: Listener): number;
|
|
387
528
|
}
|
|
388
529
|
|
|
389
530
|
interface LoroTree<
|
|
@@ -395,7 +536,7 @@ declare module "loro-wasm" {
|
|
|
395
536
|
delete(target: TreeID): void;
|
|
396
537
|
has(target: TreeID): boolean;
|
|
397
538
|
getNodeByID(target: TreeID): LoroTreeNode;
|
|
398
|
-
subscribe(
|
|
539
|
+
subscribe(listener: Listener): number;
|
|
399
540
|
}
|
|
400
541
|
|
|
401
542
|
interface LoroTreeNode<
|