loro-crdt 1.8.9 → 1.9.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.
@@ -1,30 +1,6 @@
1
1
  /* tslint:disable */
2
2
  /* eslint-disable */
3
- /**
4
- * Get the version of Loro
5
- */
6
- export function LORO_VERSION(): string;
7
- export function run(): void;
8
- export function encodeFrontiers(frontiers: ({ peer: PeerID, counter: number })[]): Uint8Array;
9
- export function decodeFrontiers(bytes: Uint8Array): { peer: PeerID, counter: number }[];
10
- /**
11
- * Enable debug info of Loro
12
- */
13
- export function setDebug(): void;
14
3
  export function callPendingEvents(): void;
15
- /**
16
- * Decode the metadata of the import blob.
17
- *
18
- * This method is useful to get the following metadata of the import blob:
19
- *
20
- * - startVersionVector
21
- * - endVersionVector
22
- * - startTimestamp
23
- * - endTimestamp
24
- * - mode
25
- * - changeNum
26
- */
27
- export function decodeImportBlobMeta(blob: Uint8Array, check_checksum: boolean): ImportBlobMetadata;
28
4
  /**
29
5
  * Redacts sensitive content in JSON updates within the specified version range.
30
6
  *
@@ -45,6 +21,30 @@ export function decodeImportBlobMeta(blob: Uint8Array, check_checksum: boolean):
45
21
  * @returns {Object} The redacted JSON updates
46
22
  */
47
23
  export function redactJsonUpdates(json_updates: string | JsonSchema, version_range: any): JsonSchema;
24
+ export function run(): void;
25
+ /**
26
+ * Decode the metadata of the import blob.
27
+ *
28
+ * This method is useful to get the following metadata of the import blob:
29
+ *
30
+ * - startVersionVector
31
+ * - endVersionVector
32
+ * - startTimestamp
33
+ * - endTimestamp
34
+ * - mode
35
+ * - changeNum
36
+ */
37
+ export function decodeImportBlobMeta(blob: Uint8Array, check_checksum: boolean): ImportBlobMetadata;
38
+ /**
39
+ * Get the version of Loro
40
+ */
41
+ export function LORO_VERSION(): string;
42
+ export function decodeFrontiers(bytes: Uint8Array): { peer: PeerID, counter: number }[];
43
+ export function encodeFrontiers(frontiers: ({ peer: PeerID, counter: number })[]): Uint8Array;
44
+ /**
45
+ * Enable debug info of Loro
46
+ */
47
+ export function setDebug(): void;
48
48
 
49
49
  /**
50
50
  * Container types supported by loro.
@@ -85,27 +85,6 @@ export type ContainerID =
85
85
  export type TreeID = `${number}@${PeerID}`;
86
86
 
87
87
  interface LoroDoc {
88
- /**
89
- * Export updates from the specific version to the current version
90
- *
91
- * @deprecated Use `export({mode: "update", from: version})` instead
92
- *
93
- * @example
94
- * ```ts
95
- * import { LoroDoc } from "loro-crdt";
96
- *
97
- * const doc = new LoroDoc();
98
- * const text = doc.getText("text");
99
- * text.insert(0, "Hello");
100
- * // get all updates of the doc
101
- * const updates = doc.exportFrom();
102
- * const version = doc.oplogVersion();
103
- * text.insert(5, " World");
104
- * // get updates from specific version to the latest version
105
- * const updates2 = doc.exportFrom(version);
106
- * ```
107
- */
108
- exportFrom(version?: VersionVector): Uint8Array;
109
88
  /**
110
89
  *
111
90
  * Get the container corresponding to the container id
@@ -1451,6 +1430,14 @@ interface EphemeralStoreEvent {
1451
1430
  */
1452
1431
  export class AwarenessWasm {
1453
1432
  free(): void;
1433
+ /**
1434
+ * Get the timestamp of the state of a given peer.
1435
+ */
1436
+ getTimestamp(peer: number | bigint | `${number}`): number | undefined;
1437
+ /**
1438
+ * Remove the states of outdated peers.
1439
+ */
1440
+ removeOutdated(): PeerID[];
1454
1441
  /**
1455
1442
  * Creates a new `Awareness` instance.
1456
1443
  *
@@ -1460,13 +1447,9 @@ export class AwarenessWasm {
1460
1447
  */
1461
1448
  constructor(peer: number | bigint | `${number}`, timeout: number);
1462
1449
  /**
1463
- * Encodes the state of the given peers.
1464
- */
1465
- encode(peers: Array<any>): Uint8Array;
1466
- /**
1467
- * Encodes the state of all peers.
1450
+ * Get the PeerID of the local peer.
1468
1451
  */
1469
- encodeAll(): Uint8Array;
1452
+ peer(): PeerID;
1470
1453
  /**
1471
1454
  * Applies the encoded state of peers.
1472
1455
  *
@@ -1475,17 +1458,13 @@ export class AwarenessWasm {
1475
1458
  */
1476
1459
  apply(encoded_peers_info: Uint8Array): { updated: PeerID[], added: PeerID[] };
1477
1460
  /**
1478
- * Get the PeerID of the local peer.
1479
- */
1480
- peer(): PeerID;
1481
- /**
1482
- * Get the timestamp of the state of a given peer.
1461
+ * Get all the peers
1483
1462
  */
1484
- getTimestamp(peer: number | bigint | `${number}`): number | undefined;
1463
+ peers(): PeerID[];
1485
1464
  /**
1486
- * Remove the states of outdated peers.
1465
+ * Encodes the state of the given peers.
1487
1466
  */
1488
- removeOutdated(): PeerID[];
1467
+ encode(peers: Array<any>): Uint8Array;
1489
1468
  /**
1490
1469
  * Get the number of peers.
1491
1470
  */
@@ -1495,9 +1474,9 @@ export class AwarenessWasm {
1495
1474
  */
1496
1475
  isEmpty(): boolean;
1497
1476
  /**
1498
- * Get all the peers
1477
+ * Encodes the state of all peers.
1499
1478
  */
1500
- peers(): PeerID[];
1479
+ encodeAll(): Uint8Array;
1501
1480
  }
1502
1481
  export class ChangeModifier {
1503
1482
  private constructor();
@@ -1552,24 +1531,27 @@ export class Cursor {
1552
1531
  */
1553
1532
  pos(): { peer: PeerID, counter: number } | undefined;
1554
1533
  /**
1555
- * Get which side of the character/list item the cursor is on.
1534
+ * "Cursor"
1556
1535
  */
1557
- side(): Side;
1536
+ kind(): any;
1558
1537
  /**
1559
- * Encode the cursor into a Uint8Array.
1538
+ * Get which side of the character/list item the cursor is on.
1560
1539
  */
1561
- encode(): Uint8Array;
1540
+ side(): Side;
1562
1541
  /**
1563
1542
  * Decode the cursor from a Uint8Array.
1564
1543
  */
1565
1544
  static decode(data: Uint8Array): Cursor;
1566
1545
  /**
1567
- * "Cursor"
1546
+ * Encode the cursor into a Uint8Array.
1568
1547
  */
1569
- kind(): any;
1548
+ encode(): Uint8Array;
1570
1549
  }
1571
1550
  export class EphemeralStoreWasm {
1572
1551
  free(): void;
1552
+ getAllStates(): any;
1553
+ removeOutdated(): void;
1554
+ get(key: string): any;
1573
1555
  /**
1574
1556
  * Creates a new `EphemeralStore` instance.
1575
1557
  *
@@ -1579,18 +1561,15 @@ export class EphemeralStoreWasm {
1579
1561
  */
1580
1562
  constructor(timeout: number);
1581
1563
  set(key: string, value: any): void;
1564
+ keys(): string[];
1565
+ apply(data: Uint8Array): void;
1582
1566
  delete(key: string): void;
1583
- get(key: string): any;
1584
- getAllStates(): any;
1585
1567
  encode(key: string): Uint8Array;
1586
- encodeAll(): Uint8Array;
1587
- apply(data: Uint8Array): void;
1588
- removeOutdated(): void;
1589
1568
  /**
1590
1569
  * If the state is empty.
1591
1570
  */
1592
1571
  isEmpty(): boolean;
1593
- keys(): string[];
1572
+ encodeAll(): Uint8Array;
1594
1573
  }
1595
1574
  /**
1596
1575
  * The handler of a counter container.
@@ -1598,25 +1577,29 @@ export class EphemeralStoreWasm {
1598
1577
  export class LoroCounter {
1599
1578
  free(): void;
1600
1579
  /**
1601
- * Create a new LoroCounter.
1580
+ * Whether the container is attached to a docuemnt.
1581
+ *
1582
+ * If it's detached, the operations on the container will not be persisted.
1602
1583
  */
1603
- constructor();
1584
+ isAttached(): boolean;
1604
1585
  /**
1605
- * "Counter"
1586
+ * Get the attached container associated with this.
1587
+ *
1588
+ * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
1606
1589
  */
1607
- kind(): 'Counter';
1590
+ getAttached(): LoroTree | undefined;
1608
1591
  /**
1609
- * Increment the counter by the given value.
1592
+ * Get the value of the counter.
1610
1593
  */
1611
- increment(value: number): void;
1594
+ getShallowValue(): number;
1612
1595
  /**
1613
- * Decrement the counter by the given value.
1596
+ * Create a new LoroCounter.
1614
1597
  */
1615
- decrement(value: number): void;
1598
+ constructor();
1616
1599
  /**
1617
- * Subscribe to the changes of the counter.
1600
+ * "Counter"
1618
1601
  */
1619
- subscribe(f: Function): any;
1602
+ kind(): 'Counter';
1620
1603
  /**
1621
1604
  * Get the parent container of the counter container.
1622
1605
  *
@@ -1625,23 +1608,19 @@ export class LoroCounter {
1625
1608
  * the WASM boundary.
1626
1609
  */
1627
1610
  parent(): Container | undefined;
1611
+ toJSON(): number;
1628
1612
  /**
1629
- * Whether the container is attached to a docuemnt.
1630
- *
1631
- * If it's detached, the operations on the container will not be persisted.
1613
+ * Decrement the counter by the given value.
1632
1614
  */
1633
- isAttached(): boolean;
1615
+ decrement(value: number): void;
1634
1616
  /**
1635
- * Get the attached container associated with this.
1636
- *
1637
- * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
1617
+ * Increment the counter by the given value.
1638
1618
  */
1639
- getAttached(): LoroTree | undefined;
1619
+ increment(value: number): void;
1640
1620
  /**
1641
- * Get the value of the counter.
1621
+ * Subscribe to the changes of the counter.
1642
1622
  */
1643
- getShallowValue(): number;
1644
- toJSON(): number;
1623
+ subscribe(f: Function): any;
1645
1624
  /**
1646
1625
  * The container id of this handler.
1647
1626
  */
@@ -1674,147 +1653,77 @@ export class LoroCounter {
1674
1653
  export class LoroDoc {
1675
1654
  free(): void;
1676
1655
  /**
1677
- * Create a new loro document.
1678
- *
1679
- * New document will have a random peer id.
1680
- */
1681
- constructor();
1682
- /**
1683
- * Enables editing in detached mode, which is disabled by default.
1656
+ * Apply a batch of diff to the document
1684
1657
  *
1685
- * The doc enter detached mode after calling `detach` or checking out a non-latest version.
1658
+ * A diff batch represents a set of changes between two versions of the document.
1659
+ * You can calculate a diff batch using `doc.diff()`.
1686
1660
  *
1687
- * # Important Notes:
1661
+ * Changes are associated with container IDs. During diff application, if new containers were created in the source
1662
+ * document, they will be assigned fresh IDs in the target document. Loro automatically handles remapping these
1663
+ * container IDs from their original IDs to the new IDs as the diff is applied.
1688
1664
  *
1689
- * - This mode uses a different PeerID for each checkout.
1690
- * - Ensure no concurrent operations share the same PeerID if set manually.
1691
- * - Importing does not affect the document's state or version; changes are
1692
- * recorded in the [OpLog] only. Call `checkout` to apply changes.
1693
- */
1694
- setDetachedEditing(enable: boolean): void;
1695
- /**
1696
- * Whether the editing is enabled in detached mode.
1665
+ * @example
1666
+ * ```ts
1667
+ * const doc1 = new LoroDoc();
1668
+ * const doc2 = new LoroDoc();
1697
1669
  *
1698
- * The doc enter detached mode after calling `detach` or checking out a non-latest version.
1670
+ * // Make some changes to doc1
1671
+ * const text = doc1.getText("text");
1672
+ * text.insert(0, "Hello");
1699
1673
  *
1700
- * # Important Notes:
1674
+ * // Calculate diff between empty and current state
1675
+ * const diff = doc1.diff([], doc1.frontiers());
1701
1676
  *
1702
- * - This mode uses a different PeerID for each checkout.
1703
- * - Ensure no concurrent operations share the same PeerID if set manually.
1704
- * - Importing does not affect the document's state or version; changes are
1705
- * recorded in the [OpLog] only. Call `checkout` to apply changes.
1677
+ * // Apply changes to doc2
1678
+ * doc2.applyDiff(diff);
1679
+ * console.log(doc2.getText("text").toString()); // "Hello"
1680
+ * ```
1706
1681
  */
1707
- isDetachedEditingEnabled(): boolean;
1682
+ applyDiff(diff: [ContainerID, Diff|JsonDiff][]): void;
1708
1683
  /**
1709
- * Set whether to record the timestamp of each change. Default is `false`.
1710
- *
1711
- * If enabled, the Unix timestamp (in seconds) will be recorded for each change automatically.
1712
- *
1713
- * You can also set each timestamp manually when you commit a change.
1714
- * The timestamp manually set will override the automatic one.
1715
- *
1716
- * NOTE: Timestamps are forced to be in ascending order in the OpLog's history.
1717
- * If you commit a new change with a timestamp that is less than the existing one,
1718
- * the largest existing timestamp will be used instead.
1684
+ * Check if the doc contains the full history.
1719
1685
  */
1720
- setRecordTimestamp(auto_record: boolean): void;
1686
+ isShallow(): boolean;
1721
1687
  /**
1722
- * If two continuous local changes are within (<=) the interval(**in seconds**), they will be merged into one change.
1723
- *
1724
- * The default value is 1_000 seconds.
1725
- *
1726
- * By default, we record timestamps in seconds for each change. So if the merge interval is 1, and changes A and B
1727
- * have timestamps of 3 and 4 respectively, then they will be merged into one change
1688
+ * Get the number of changes in the oplog.
1728
1689
  */
1729
- setChangeMergeInterval(interval: number): void;
1690
+ changeCount(): number;
1730
1691
  /**
1731
- * Set the rich text format configuration of the document.
1692
+ * Get the value or container at the given path
1732
1693
  *
1733
- * You need to config it if you use rich text `mark` method.
1734
- * Specifically, you need to config the `expand` property of each style.
1694
+ * The path can be specified in different ways depending on the container type:
1735
1695
  *
1736
- * Expand is used to specify the behavior of expanding when new text is inserted at the
1737
- * beginning or end of the style.
1696
+ * For Tree:
1697
+ * 1. Using node IDs: `tree/{node_id}/property`
1698
+ * 2. Using indices: `tree/0/1/property`
1738
1699
  *
1739
- * You can specify the `expand` option to set the behavior when inserting text at the boundary of the range.
1700
+ * For List and MovableList:
1701
+ * - Using indices: `list/0` or `list/1/property`
1740
1702
  *
1741
- * - `after`(default): when inserting text right after the given range, the mark will be expanded to include the inserted text
1742
- * - `before`: when inserting text right before the given range, the mark will be expanded to include the inserted text
1743
- * - `none`: the mark will not be expanded to include the inserted text at the boundaries
1744
- * - `both`: when inserting text either right before or right after the given range, the mark will be expanded to include the inserted text
1703
+ * For Map:
1704
+ * - Using keys: `map/key` or `map/nested/property`
1705
+ *
1706
+ * For tree structures, index-based paths follow depth-first traversal order.
1707
+ * The indices start from 0 and represent the position of a node among its siblings.
1745
1708
  *
1746
1709
  * @example
1747
1710
  * ```ts
1711
+ * import { LoroDoc } from "loro-crdt";
1712
+ *
1748
1713
  * const doc = new LoroDoc();
1749
- * doc.configTextStyle({
1750
- * bold: { expand: "after" },
1751
- * link: { expand: "before" }
1752
- * });
1753
- * const text = doc.getText("text");
1754
- * text.insert(0, "Hello World!");
1755
- * text.mark({ start: 0, end: 5 }, "bold", true);
1756
- * expect(text.toDelta()).toStrictEqual([
1757
- * {
1758
- * insert: "Hello",
1759
- * attributes: {
1760
- * bold: true,
1761
- * },
1762
- * },
1763
- * {
1764
- * insert: " World!",
1765
- * },
1766
- * ] as Delta<string>[]);
1767
- * ```
1768
- */
1769
- configTextStyle(styles: {[key: string]: { expand: 'before'|'after'|'none'|'both' }}): void;
1770
- /**
1771
- * Configures the default text style for the document.
1772
- *
1773
- * This method sets the default text style configuration for the document when using LoroText.
1774
- * If `None` is provided, the default style is reset.
1775
- */
1776
- configDefaultTextStyle(style: { expand: 'before'|'after'|'none'|'both' } | undefined): void;
1777
- /**
1778
- * Create a loro document from the snapshot.
1779
- *
1780
- * @see You can learn more [here](https://loro.dev/docs/tutorial/encoding).
1781
- *
1782
- * @example
1783
- * ```ts
1784
- * import { LoroDoc } from "loro-crdt"
1785
- *
1786
- * const doc = new LoroDoc();
1787
- * // ...
1788
- * const bytes = doc.export({ mode: "snapshot" });
1789
- * const loro = LoroDoc.fromSnapshot(bytes);
1714
+ * const map = doc.getMap("map");
1715
+ * map.set("key", 1);
1716
+ * console.log(doc.getByPath("map/key")); // 1
1717
+ * console.log(doc.getByPath("map")); // LoroMap
1790
1718
  * ```
1791
1719
  */
1792
- static fromSnapshot(snapshot: Uint8Array): LoroDoc;
1720
+ getByPath(path: string): Value | Container | undefined;
1793
1721
  /**
1794
- * Attach the document state to the latest known version.
1795
- *
1796
- * > The document becomes detached during a `checkout` operation.
1797
- * > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
1798
- * > In a detached state, the document is not editable, and any `import` operations will be
1799
- * > recorded in the `OpLog` without being applied to the `DocState`.
1800
- *
1801
- * This method has the same effect as invoking `checkoutToLatest`.
1802
- *
1803
- * @example
1804
- * ```ts
1805
- * import { LoroDoc } from "loro-crdt";
1722
+ * Get a LoroCounter by container id
1806
1723
  *
1807
- * const doc = new LoroDoc();
1808
- * const text = doc.getText("text");
1809
- * const frontiers = doc.frontiers();
1810
- * text.insert(0, "Hello World!");
1811
- * doc.checkout(frontiers);
1812
- * // you need call `attach()` or `checkoutToLatest()` before changing the doc.
1813
- * doc.attach();
1814
- * text.insert(0, "Hi");
1815
- * ```
1724
+ * If the container does not exist, an error will be thrown.
1816
1725
  */
1817
- attach(): void;
1726
+ getCounter(cid: ContainerID | string): LoroCounter;
1818
1727
  /**
1819
1728
  * `detached` indicates that the `DocState` is not synchronized with the latest version of `OpLog`.
1820
1729
  *
@@ -1840,45 +1749,67 @@ export class LoroDoc {
1840
1749
  */
1841
1750
  isDetached(): boolean;
1842
1751
  /**
1843
- * Detach the document state from the latest known version.
1752
+ * Set the peer ID of the current writer.
1844
1753
  *
1845
- * After detaching, all import operations will be recorded in the `OpLog` without being applied to the `DocState`.
1846
- * When `detached`, the document is not editable.
1754
+ * It must be a number, a BigInt, or a decimal string that can be parsed to a unsigned 64-bit integer.
1755
+ *
1756
+ * Note: use it with caution. You need to make sure there is not chance that two peers
1757
+ * have the same peer ID. Otherwise, we cannot ensure the consistency of the document.
1758
+ */
1759
+ setPeerId(peer_id: number | bigint | `${number}`): void;
1760
+ /**
1761
+ * Get the absolute position of the given Cursor
1847
1762
  *
1848
1763
  * @example
1849
1764
  * ```ts
1850
- * import { LoroDoc } from "loro-crdt";
1851
- *
1852
1765
  * const doc = new LoroDoc();
1853
- * doc.detach();
1854
- * console.log(doc.isDetached()); // true
1766
+ * const text = doc.getText("text");
1767
+ * text.insert(0, "123");
1768
+ * const pos0 = text.getCursor(0, 0);
1769
+ * {
1770
+ * const ans = doc.getCursorPos(pos0!);
1771
+ * expect(ans.offset).toBe(0);
1772
+ * }
1773
+ * text.insert(0, "1");
1774
+ * {
1775
+ * const ans = doc.getCursorPos(pos0!);
1776
+ * expect(ans.offset).toBe(1);
1777
+ * }
1855
1778
  * ```
1856
1779
  */
1857
- detach(): void;
1780
+ getCursorPos(cursor: Cursor): { update?: Cursor, offset: number, side: Side } | undefined;
1858
1781
  /**
1859
- * Duplicate the document with a different PeerID
1782
+ * Check if the doc contains the target container.
1860
1783
  *
1861
- * The time complexity and space complexity of this operation are both O(n),
1784
+ * A root container always exists, while a normal container exists
1785
+ * if it has ever been created on the doc.
1862
1786
  *
1863
- * When called in detached mode, it will fork at the current state frontiers.
1864
- * It will have the same effect as `forkAt(&self.frontiers())`.
1865
- */
1866
- fork(): LoroDoc;
1867
- /**
1868
- * Creates a new LoroDoc at a specified version (Frontiers)
1787
+ * @example
1788
+ * ```ts
1789
+ * import { LoroDoc, LoroMap, LoroText, LoroList } from "loro-crdt";
1869
1790
  *
1870
- * The created doc will only contain the history before the specified frontiers.
1791
+ * const doc = new LoroDoc();
1792
+ * doc.setPeerId("1");
1793
+ * const text = doc.getMap("map").setContainer("text", new LoroText());
1794
+ * const list = doc.getMap("map").setContainer("list", new LoroList());
1795
+ * expect(doc.isContainerExists("cid:root-map:Map")).toBe(true);
1796
+ * expect(doc.isContainerExists("cid:0@1:Text")).toBe(true);
1797
+ * expect(doc.isContainerExists("cid:1@1:List")).toBe(true);
1798
+ *
1799
+ * const doc2 = new LoroDoc();
1800
+ * // Containers exist, as long as the history or the doc state include it
1801
+ * doc.detach();
1802
+ * doc2.import(doc.export({ mode: "update" }));
1803
+ * expect(doc2.isContainerExists("cid:root-map:Map")).toBe(true);
1804
+ * expect(doc2.isContainerExists("cid:0@1:Text")).toBe(true);
1805
+ * expect(doc2.isContainerExists("cid:1@1:List")).toBe(true);
1806
+ * ```
1871
1807
  */
1872
- forkAt(frontiers: ({ peer: PeerID, counter: number })[]): LoroDoc;
1808
+ hasContainer(container_id: ContainerID): boolean;
1873
1809
  /**
1874
- * Checkout the `DocState` to the latest version of `OpLog`.
1875
- *
1876
- * > The document becomes detached during a `checkout` operation.
1877
- * > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
1878
- * > In a detached state, the document is not editable by default, and any `import` operations will be
1879
- * > recorded in the `OpLog` without being applied to the `DocState`.
1810
+ * Import a batch of updates or snapshots.
1880
1811
  *
1881
- * This has the same effect as `attach`.
1812
+ * It's more efficient than importing updates one by one.
1882
1813
  *
1883
1814
  * @example
1884
1815
  * ```ts
@@ -1886,78 +1817,61 @@ export class LoroDoc {
1886
1817
  *
1887
1818
  * const doc = new LoroDoc();
1888
1819
  * const text = doc.getText("text");
1889
- * const frontiers = doc.frontiers();
1890
- * text.insert(0, "Hello World!");
1891
- * doc.checkout(frontiers);
1892
- * // you need call `checkoutToLatest()` or `attach()` before changing the doc.
1893
- * doc.checkoutToLatest();
1894
- * text.insert(0, "Hi");
1820
+ * text.insert(0, "Hello");
1821
+ * const updates = doc.export({ mode: "update" });
1822
+ * const snapshot = doc.export({ mode: "snapshot" });
1823
+ * const doc2 = new LoroDoc();
1824
+ * doc2.importBatch([snapshot, updates]);
1895
1825
  * ```
1896
1826
  */
1897
- checkoutToLatest(): void;
1827
+ importBatch(data: Uint8Array[]): ImportStatus;
1898
1828
  /**
1899
- * Visit all the ancestors of the changes in causal order.
1829
+ * Compare the ordering of two Frontiers.
1900
1830
  *
1901
- * @param ids - the changes to visit
1902
- * @param f - the callback function, return `true` to continue visiting, return `false` to stop
1831
+ * It's assumed that both Frontiers are included by the doc. Otherwise, an error will be thrown.
1832
+ *
1833
+ * Return value:
1834
+ *
1835
+ * - -1: a < b
1836
+ * - 0: a == b
1837
+ * - 1: a > b
1838
+ * - undefined: a ∥ b: a and b are concurrent
1903
1839
  */
1904
- travelChangeAncestors(ids: ({ peer: PeerID, counter: number })[], f: (change: Change) => boolean): void;
1840
+ cmpFrontiers(a: ({ peer: PeerID, counter: number })[], b: ({ peer: PeerID, counter: number })[]): -1 | 1 | 0 | undefined;
1905
1841
  /**
1906
- * Find the op id spans that between the `from` version and the `to` version.
1907
- *
1908
- * You can combine it with `exportJsonInIdSpan` to get the changes between two versions.
1842
+ * Debug the size of the history
1843
+ */
1844
+ debugHistory(): void;
1845
+ /**
1846
+ * Create a loro document from the snapshot.
1909
1847
  *
1910
- * You can use it to travel all the changes from `from` to `to`. `from` and `to` are frontiers,
1911
- * and they can be concurrent to each other. You can use it to find all the changes related to an event:
1848
+ * @see You can learn more [here](https://loro.dev/docs/tutorial/encoding).
1912
1849
  *
1913
1850
  * @example
1914
1851
  * ```ts
1915
- * import { LoroDoc } from "loro-crdt";
1916
- *
1917
- * const docA = new LoroDoc();
1918
- * docA.setPeerId("1");
1919
- * const docB = new LoroDoc();
1852
+ * import { LoroDoc } from "loro-crdt"
1920
1853
  *
1921
- * docA.getText("text").update("Hello");
1922
- * docA.commit();
1923
- * const snapshot = docA.export({ mode: "snapshot" });
1924
- * let done = false;
1925
- * docB.subscribe(e => {
1926
- * const spans = docB.findIdSpansBetween(e.from, e.to);
1927
- * const changes = docB.exportJsonInIdSpan(spans.forward[0]);
1928
- * console.log(changes);
1929
- * // [{
1930
- * // id: "0@1",
1931
- * // timestamp: expect.any(Number),
1932
- * // deps: [],
1933
- * // lamport: 0,
1934
- * // msg: undefined,
1935
- * // ops: [{
1936
- * // container: "cid:root-text:Text",
1937
- * // counter: 0,
1938
- * // content: {
1939
- * // type: "insert",
1940
- * // pos: 0,
1941
- * // text: "Hello"
1942
- * // }
1943
- * // }]
1944
- * // }]
1945
- * });
1946
- * docB.import(snapshot);
1854
+ * const doc = new LoroDoc();
1855
+ * // ...
1856
+ * const bytes = doc.export({ mode: "snapshot" });
1857
+ * const loro = LoroDoc.fromSnapshot(bytes);
1947
1858
  * ```
1948
1859
  */
1949
- findIdSpansBetween(from: ({ peer: PeerID, counter: number })[], to: ({ peer: PeerID, counter: number })[]): VersionVectorDiff;
1860
+ static fromSnapshot(snapshot: Uint8Array): LoroDoc;
1950
1861
  /**
1951
- * Checkout the `DocState` to a specific version.
1952
- *
1953
- * > The document becomes detached during a `checkout` operation.
1954
- * > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
1955
- * > In a detached state, the document is not editable, and any `import` operations will be
1956
- * > recorded in the `OpLog` without being applied to the `DocState`.
1862
+ * Get the change that contains the specific ID
1863
+ */
1864
+ getChangeAt(id: { peer: PeerID, counter: number }): Change;
1865
+ /**
1866
+ * Get the version vector of the latest known version in OpLog.
1957
1867
  *
1958
- * You should call `attach` to attach the `DocState` to the latest version of `OpLog`.
1868
+ * If you checkout to a specific version, this version vector will not change.
1869
+ */
1870
+ oplogVersion(): VersionVector;
1871
+ /**
1872
+ * Convert frontiers to a version vector
1959
1873
  *
1960
- * @param frontiers - the specific frontiers
1874
+ * Learn more about frontiers and version vector [here](https://loro.dev/docs/advanced/version_deep_dive)
1961
1875
  *
1962
1876
  * @example
1963
1877
  * ```ts
@@ -1965,67 +1879,43 @@ export class LoroDoc {
1965
1879
  *
1966
1880
  * const doc = new LoroDoc();
1967
1881
  * const text = doc.getText("text");
1882
+ * text.insert(0, "Hello");
1968
1883
  * const frontiers = doc.frontiers();
1969
- * text.insert(0, "Hello World!");
1970
- * doc.checkout(frontiers);
1971
- * console.log(doc.toJSON()); // {"text": ""}
1884
+ * const version = doc.frontiersToVV(frontiers);
1972
1885
  * ```
1973
1886
  */
1974
- checkout(frontiers: ({ peer: PeerID, counter: number })[]): void;
1887
+ frontiersToVV(frontiers: ({ peer: PeerID, counter: number })[]): VersionVector;
1975
1888
  /**
1976
- * Set the peer ID of the current writer.
1889
+ * Get all of changes in the oplog.
1977
1890
  *
1978
- * It must be a number, a BigInt, or a decimal string that can be parsed to a unsigned 64-bit integer.
1891
+ * Note: this method is expensive when the oplog is large. O(n)
1979
1892
  *
1980
- * Note: use it with caution. You need to make sure there is not chance that two peers
1981
- * have the same peer ID. Otherwise, we cannot ensure the consistency of the document.
1982
- */
1983
- setPeerId(peer_id: number | bigint | `${number}`): void;
1984
- /**
1985
- * Commit the cumulative auto-committed transaction.
1893
+ * @example
1894
+ * ```ts
1895
+ * import { LoroDoc, LoroText } from "loro-crdt";
1986
1896
  *
1987
- * You can specify the `origin`, `timestamp`, and `message` of the commit.
1897
+ * const doc = new LoroDoc();
1898
+ * const text = doc.getText("text");
1899
+ * text.insert(0, "Hello");
1900
+ * const changes = doc.getAllChanges();
1988
1901
  *
1989
- * - The `origin` is used to mark the event
1990
- * - The `message` works like a git commit message, which will be recorded and synced to peers
1991
- * - The `timestamp` is the number of seconds that have elapsed since 00:00:00 UTC on January 1, 1970.
1992
- * It defaults to `Date.now() / 1000` when timestamp recording is enabled
1993
- *
1994
- * The events will be emitted after a transaction is committed. A transaction is committed when:
1995
- *
1996
- * - `doc.commit()` is called.
1997
- * - `doc.export(mode)` is called.
1998
- * - `doc.import(data)` is called.
1999
- * - `doc.checkout(version)` is called.
2000
- *
2001
- * NOTE: Timestamps are forced to be in ascending order.
2002
- * If you commit a new change with a timestamp that is less than the existing one,
2003
- * the largest existing timestamp will be used instead.
2004
- *
2005
- * NOTE: The `origin` will not be persisted, but the `message` will.
2006
- *
2007
- * Behavior on empty commits:
2008
- * - This method is an explicit commit. If the pending transaction is empty, any provided
2009
- * options (message/timestamp/origin) are swallowed and will not carry over to the next commit.
2010
- * - Implicit commits triggered by `export`/`checkout` act as processing barriers. If the
2011
- * transaction is empty in those cases, `message`/`timestamp`/`origin` are preserved for the
2012
- * next commit.
1902
+ * for (let [peer, c] of changes.entries()){
1903
+ * console.log("peer: ", peer);
1904
+ * for (let change of c){
1905
+ * console.log("change: ", change);
1906
+ * }
1907
+ * }
1908
+ * ```
2013
1909
  */
2014
- commit(options?: { origin?: string, timestamp?: number, message?: string } | null): void;
1910
+ getAllChanges(): Map<PeerID, Change[]>;
2015
1911
  /**
2016
- * Get the number of operations in the pending transaction.
1912
+ * Get the [frontiers](https://loro.dev/docs/advanced/version_deep_dive) of the latest version in OpLog.
2017
1913
  *
2018
- * The pending transaction is the one that is not committed yet. It will be committed
2019
- * automatically after calling `doc.commit()`, `doc.export(mode)` or `doc.checkout(version)`.
1914
+ * If you checkout to a specific version, this value will not change.
2020
1915
  */
2021
- getPendingTxnLength(): number;
1916
+ oplogFrontiers(): { peer: PeerID, counter: number }[];
2022
1917
  /**
2023
- * Get a LoroText by container id.
2024
- *
2025
- * The object returned is a new js object each time because it need to cross
2026
- * the WASM boundary.
2027
- *
2028
- * If the container does not exist, an error will be thrown.
1918
+ * Convert a version vector to frontiers
2029
1919
  *
2030
1920
  * @example
2031
1921
  * ```ts
@@ -2033,119 +1923,116 @@ export class LoroDoc {
2033
1923
  *
2034
1924
  * const doc = new LoroDoc();
2035
1925
  * const text = doc.getText("text");
1926
+ * text.insert(0, "Hello");
1927
+ * const version = doc.version();
1928
+ * const frontiers = doc.vvToFrontiers(version);
2036
1929
  * ```
2037
1930
  */
2038
- getText(cid: ContainerID | string): LoroText;
1931
+ vvToFrontiers(vv: VersionVector): { peer: PeerID, counter: number }[];
2039
1932
  /**
2040
- * Get a LoroCounter by container id
1933
+ * The doc only contains the history since this version
2041
1934
  *
2042
- * If the container does not exist, an error will be thrown.
1935
+ * This is empty if the doc is not shallow.
1936
+ *
1937
+ * The ops included by the shallow history start version vector are not in the doc.
2043
1938
  */
2044
- getCounter(cid: ContainerID | string): LoroCounter;
1939
+ shallowSinceVV(): VersionVector;
2045
1940
  /**
2046
- * Check if the doc contains the target container.
1941
+ * Set the rich text format configuration of the document.
2047
1942
  *
2048
- * A root container always exists, while a normal container exists
2049
- * if it has ever been created on the doc.
1943
+ * You need to config it if you use rich text `mark` method.
1944
+ * Specifically, you need to config the `expand` property of each style.
1945
+ *
1946
+ * Expand is used to specify the behavior of expanding when new text is inserted at the
1947
+ * beginning or end of the style.
1948
+ *
1949
+ * You can specify the `expand` option to set the behavior when inserting text at the boundary of the range.
1950
+ *
1951
+ * - `after`(default): when inserting text right after the given range, the mark will be expanded to include the inserted text
1952
+ * - `before`: when inserting text right before the given range, the mark will be expanded to include the inserted text
1953
+ * - `none`: the mark will not be expanded to include the inserted text at the boundaries
1954
+ * - `both`: when inserting text either right before or right after the given range, the mark will be expanded to include the inserted text
2050
1955
  *
2051
1956
  * @example
2052
1957
  * ```ts
2053
- * import { LoroDoc, LoroMap, LoroText, LoroList } from "loro-crdt";
2054
- *
2055
1958
  * const doc = new LoroDoc();
2056
- * doc.setPeerId("1");
2057
- * const text = doc.getMap("map").setContainer("text", new LoroText());
2058
- * const list = doc.getMap("map").setContainer("list", new LoroList());
2059
- * expect(doc.isContainerExists("cid:root-map:Map")).toBe(true);
2060
- * expect(doc.isContainerExists("cid:0@1:Text")).toBe(true);
2061
- * expect(doc.isContainerExists("cid:1@1:List")).toBe(true);
2062
- *
2063
- * const doc2 = new LoroDoc();
2064
- * // Containers exist, as long as the history or the doc state include it
2065
- * doc.detach();
2066
- * doc2.import(doc.export({ mode: "update" }));
2067
- * expect(doc2.isContainerExists("cid:root-map:Map")).toBe(true);
2068
- * expect(doc2.isContainerExists("cid:0@1:Text")).toBe(true);
2069
- * expect(doc2.isContainerExists("cid:1@1:List")).toBe(true);
1959
+ * doc.configTextStyle({
1960
+ * bold: { expand: "after" },
1961
+ * link: { expand: "before" }
1962
+ * });
1963
+ * const text = doc.getText("text");
1964
+ * text.insert(0, "Hello World!");
1965
+ * text.mark({ start: 0, end: 5 }, "bold", true);
1966
+ * expect(text.toDelta()).toStrictEqual([
1967
+ * {
1968
+ * insert: "Hello",
1969
+ * attributes: {
1970
+ * bold: true,
1971
+ * },
1972
+ * },
1973
+ * {
1974
+ * insert: " World!",
1975
+ * },
1976
+ * ] as Delta<string>[]);
2070
1977
  * ```
2071
1978
  */
2072
- hasContainer(container_id: ContainerID): boolean;
2073
- /**
2074
- * Set the commit message of the next commit
2075
- */
2076
- setNextCommitMessage(msg: string): void;
2077
- /**
2078
- * Set the origin of the next commit
2079
- */
2080
- setNextCommitOrigin(origin: string): void;
2081
- /**
2082
- * Set the timestamp of the next commit
2083
- */
2084
- setNextCommitTimestamp(timestamp: number): void;
2085
- /**
2086
- * Set the options of the next commit
2087
- */
2088
- setNextCommitOptions(options: { origin?: string, timestamp?: number, message?: string }): void;
2089
- /**
2090
- * Clear the options of the next commit
2091
- */
2092
- clearNextCommitOptions(): void;
2093
- /**
2094
- * Get deep value of the document with container id
2095
- */
2096
- getDeepValueWithID(): any;
2097
- /**
2098
- * Get the path from the root to the container
2099
- */
2100
- getPathToContainer(id: ContainerID): (string|number)[] | undefined;
2101
- /**
2102
- * Evaluate JSONPath against a LoroDoc
2103
- */
2104
- JSONPath(jsonpath: string): Array<any>;
1979
+ configTextStyle(styles: {[key: string]: { expand: 'before'|'after'|'none'|'both' }}): void;
2105
1980
  /**
2106
- * Get the version vector of the current document state.
2107
- *
2108
- * If you checkout to a specific version, the version vector will change.
1981
+ * Get all ops of the change that contains the specific ID
2109
1982
  */
2110
- version(): VersionVector;
1983
+ getOpsInChange(id: { peer: PeerID, counter: number }): any[];
2111
1984
  /**
2112
- * The doc only contains the history since this version
1985
+ * Get the shallow json format of the document state.
2113
1986
  *
2114
- * This is empty if the doc is not shallow.
1987
+ * Unlike `toJSON()` which recursively resolves all containers to their values,
1988
+ * `getShallowValue()` returns container IDs as strings for any nested containers.
2115
1989
  *
2116
- * The ops included by the shallow history start version vector are not in the doc.
2117
- */
2118
- shallowSinceVV(): VersionVector;
2119
- /**
2120
- * Check if the doc contains the full history.
2121
- */
2122
- isShallow(): boolean;
2123
- /**
2124
- * The doc only contains the history since this version
1990
+ * @example
1991
+ * ```ts
1992
+ * import { LoroDoc } from "loro-crdt";
2125
1993
  *
2126
- * This is empty if the doc is not shallow.
1994
+ * const doc = new LoroDoc();
1995
+ * const list = doc.getList("list");
1996
+ * const tree = doc.getTree("tree");
1997
+ * const map = doc.getMap("map");
1998
+ * const shallowValue = doc.getShallowValue();
1999
+ * console.log(shallowValue);
2000
+ * // {
2001
+ * // list: 'cid:root-list:List',
2002
+ * // tree: 'cid:root-tree:Tree',
2003
+ * // map: 'cid:root-map:Map'
2004
+ * // }
2127
2005
  *
2128
- * The ops included by the shallow history start frontiers are not in the doc.
2006
+ * // It points to the same container as `list`
2007
+ * const listB = doc.getContainerById(shallowValue.list);
2008
+ * ```
2129
2009
  */
2130
- shallowSinceFrontiers(): { peer: PeerID, counter: number }[];
2010
+ getShallowValue(): Record<string, ContainerID>;
2131
2011
  /**
2132
- * Get the version vector of the latest known version in OpLog.
2012
+ * Checkout the `DocState` to the latest version of `OpLog`.
2133
2013
  *
2134
- * If you checkout to a specific version, this version vector will not change.
2135
- */
2136
- oplogVersion(): VersionVector;
2137
- /**
2138
- * Get the [frontiers](https://loro.dev/docs/advanced/version_deep_dive) of the current document state.
2014
+ * > The document becomes detached during a `checkout` operation.
2015
+ * > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
2016
+ * > In a detached state, the document is not editable by default, and any `import` operations will be
2017
+ * > recorded in the `OpLog` without being applied to the `DocState`.
2139
2018
  *
2140
- * If you checkout to a specific version, this value will change.
2141
- */
2142
- frontiers(): { peer: PeerID, counter: number }[];
2143
- /**
2144
- * Get the [frontiers](https://loro.dev/docs/advanced/version_deep_dive) of the latest version in OpLog.
2019
+ * This has the same effect as `attach`.
2145
2020
  *
2146
- * If you checkout to a specific version, this value will not change.
2021
+ * @example
2022
+ * ```ts
2023
+ * import { LoroDoc } from "loro-crdt";
2024
+ *
2025
+ * const doc = new LoroDoc();
2026
+ * const text = doc.getText("text");
2027
+ * const frontiers = doc.frontiers();
2028
+ * text.insert(0, "Hello World!");
2029
+ * doc.checkout(frontiers);
2030
+ * // you need call `checkoutToLatest()` or `attach()` before changing the doc.
2031
+ * doc.checkoutToLatest();
2032
+ * text.insert(0, "Hi");
2033
+ * ```
2147
2034
  */
2148
- oplogFrontiers(): { peer: PeerID, counter: number }[];
2035
+ checkoutToLatest(): void;
2149
2036
  /**
2150
2037
  * Compare the version of the OpLog with the specified frontiers.
2151
2038
  *
@@ -2165,235 +2052,267 @@ export class LoroDoc {
2165
2052
  */
2166
2053
  cmpWithFrontiers(frontiers: ({ peer: PeerID, counter: number })[]): number;
2167
2054
  /**
2168
- * Compare the ordering of two Frontiers.
2055
+ * Delete all content from a root container and hide it from the document.
2169
2056
  *
2170
- * It's assumed that both Frontiers are included by the doc. Otherwise, an error will be thrown.
2057
+ * When a root container is empty and hidden:
2058
+ * - It won't show up in `get_deep_value()` results
2059
+ * - It won't be included in document snapshots
2171
2060
  *
2172
- * Return value:
2061
+ * Only works on root containers (containers without parents).
2062
+ */
2063
+ deleteRootContainer(cid: ContainerID): void;
2064
+ /**
2065
+ * Get the number of operations in the pending transaction.
2173
2066
  *
2174
- * - -1: a < b
2175
- * - 0: a == b
2176
- * - 1: a > b
2177
- * - undefined: a ∥ b: a and b are concurrent
2067
+ * The pending transaction is the one that is not committed yet. It will be committed
2068
+ * automatically after calling `doc.commit()`, `doc.export(mode)` or `doc.checkout(version)`.
2178
2069
  */
2179
- cmpFrontiers(a: ({ peer: PeerID, counter: number })[], b: ({ peer: PeerID, counter: number })[]): -1 | 1 | 0 | undefined;
2070
+ getPendingTxnLength(): number;
2180
2071
  /**
2181
- * Export the snapshot of current version.
2182
- * It includes all the history and the document state
2072
+ * Import updates from the JSON format.
2183
2073
  *
2184
- * @deprecated Use `export({mode: "snapshot"})` instead
2074
+ * only supports backward compatibility but not forward compatibility.
2185
2075
  */
2186
- exportSnapshot(): Uint8Array;
2076
+ importJsonUpdates(json: string | JsonSchema): ImportStatus;
2187
2077
  /**
2188
- * Export the document based on the specified ExportMode.
2078
+ * Import a batch of updates and snapshots.
2189
2079
  *
2190
- * @param mode - The export mode to use. Can be one of:
2191
- * - `{ mode: "snapshot" }`: Export a full snapshot of the document.
2192
- * - `{ mode: "update", from?: VersionVector }`: Export updates from the given version vector.
2193
- * If `from` is not provided, it will export the whole history of the document.
2194
- * - `{ mode: "updates-in-range", spans: { id: ID, len: number }[] }`: Export updates within the specified ID spans.
2195
- * - `{ mode: "shallow-snapshot", frontiers: Frontiers }`: Export a garbage-collected snapshot up to the given frontiers.
2080
+ * It's more efficient than importing updates one by one.
2196
2081
  *
2197
- * @returns A byte array containing the exported data.
2082
+ * @deprecated Use `importBatch` instead.
2198
2083
  *
2199
2084
  * @example
2200
2085
  * ```ts
2201
- * import { LoroDoc, LoroText } from "loro-crdt";
2086
+ * import { LoroDoc } from "loro-crdt";
2202
2087
  *
2203
2088
  * const doc = new LoroDoc();
2204
- * doc.setPeerId("1");
2205
- * doc.getText("text").update("Hello World");
2206
- *
2207
- * // Export a full snapshot
2208
- * const snapshotBytes = doc.export({ mode: "snapshot" });
2209
- *
2210
- * // Export updates from a specific version
2211
- * const vv = doc.oplogVersion();
2212
- * doc.getText("text").update("Hello Loro");
2213
- * const updateBytes = doc.export({ mode: "update", from: vv });
2214
- *
2215
- * // Export a shallow snapshot that only includes the history since the frontiers
2216
- * const shallowBytes = doc.export({ mode: "shallow-snapshot", frontiers: doc.oplogFrontiers() });
2217
- *
2218
- * // Export updates within specific ID spans
2219
- * const spanBytes = doc.export({
2220
- * mode: "updates-in-range",
2221
- * spans: [{ id: { peer: "1", counter: 0 }, len: 10 }]
2222
- * });
2089
+ * const text = doc.getText("text");
2090
+ * text.insert(0, "Hello");
2091
+ * const updates = doc.export({ mode: "update" });
2092
+ * const snapshot = doc.export({ mode: "snapshot" });
2093
+ * const doc2 = new LoroDoc();
2094
+ * doc2.importBatch([snapshot, updates]);
2223
2095
  * ```
2224
2096
  */
2225
- export(mode: ExportMode): Uint8Array;
2097
+ importUpdateBatch(data: Uint8Array[]): ImportStatus;
2226
2098
  /**
2227
- * Import updates from the JSON format.
2099
+ * Enables editing in detached mode, which is disabled by default.
2228
2100
  *
2229
- * only supports backward compatibility but not forward compatibility.
2101
+ * The doc enter detached mode after calling `detach` or checking out a non-latest version.
2102
+ *
2103
+ * # Important Notes:
2104
+ *
2105
+ * - This mode uses a different PeerID for each checkout.
2106
+ * - Ensure no concurrent operations share the same PeerID if set manually.
2107
+ * - Importing does not affect the document's state or version; changes are
2108
+ * recorded in the [OpLog] only. Call `checkout` to apply changes.
2230
2109
  */
2231
- importJsonUpdates(json: string | JsonSchema): ImportStatus;
2110
+ setDetachedEditing(enable: boolean): void;
2232
2111
  /**
2233
- * Import snapshot or updates into current doc.
2112
+ * Set whether to record the timestamp of each change. Default is `false`.
2234
2113
  *
2235
- * Note:
2236
- * - Updates within the current version will be ignored
2237
- * - Updates with missing dependencies will be pending until the dependencies are received
2114
+ * If enabled, the Unix timestamp (in seconds) will be recorded for each change automatically.
2238
2115
  *
2239
- * @example
2240
- * ```ts
2241
- * import { LoroDoc } from "loro-crdt";
2116
+ * You can also set each timestamp manually when you commit a change.
2117
+ * The timestamp manually set will override the automatic one.
2242
2118
  *
2243
- * const doc = new LoroDoc();
2244
- * const text = doc.getText("text");
2245
- * text.insert(0, "Hello");
2246
- * // get all updates of the doc
2247
- * const updates = doc.export({ mode: "update" });
2248
- * const snapshot = doc.export({ mode: "snapshot" });
2249
- * const doc2 = new LoroDoc();
2250
- * // import snapshot
2251
- * doc2.import(snapshot);
2252
- * // or import updates
2253
- * doc2.import(updates);
2254
- * ```
2119
+ * NOTE: Timestamps are forced to be in ascending order in the OpLog's history.
2120
+ * If you commit a new change with a timestamp that is less than the existing one,
2121
+ * the largest existing timestamp will be used instead.
2255
2122
  */
2256
- import(update_or_snapshot: Uint8Array): ImportStatus;
2123
+ setRecordTimestamp(auto_record: boolean): void;
2257
2124
  /**
2258
- * Import a batch of updates and snapshots.
2125
+ * Find the op id spans that between the `from` version and the `to` version.
2259
2126
  *
2260
- * It's more efficient than importing updates one by one.
2127
+ * You can combine it with `exportJsonInIdSpan` to get the changes between two versions.
2261
2128
  *
2262
- * @deprecated Use `importBatch` instead.
2129
+ * You can use it to travel all the changes from `from` to `to`. `from` and `to` are frontiers,
2130
+ * and they can be concurrent to each other. You can use it to find all the changes related to an event:
2263
2131
  *
2264
2132
  * @example
2265
2133
  * ```ts
2266
2134
  * import { LoroDoc } from "loro-crdt";
2267
2135
  *
2268
- * const doc = new LoroDoc();
2269
- * const text = doc.getText("text");
2270
- * text.insert(0, "Hello");
2271
- * const updates = doc.export({ mode: "update" });
2272
- * const snapshot = doc.export({ mode: "snapshot" });
2273
- * const doc2 = new LoroDoc();
2274
- * doc2.importBatch([snapshot, updates]);
2136
+ * const docA = new LoroDoc();
2137
+ * docA.setPeerId("1");
2138
+ * const docB = new LoroDoc();
2139
+ *
2140
+ * docA.getText("text").update("Hello");
2141
+ * docA.commit();
2142
+ * const snapshot = docA.export({ mode: "snapshot" });
2143
+ * let done = false;
2144
+ * docB.subscribe(e => {
2145
+ * const spans = docB.findIdSpansBetween(e.from, e.to);
2146
+ * const changes = docB.exportJsonInIdSpan(spans.forward[0]);
2147
+ * console.log(changes);
2148
+ * // [{
2149
+ * // id: "0@1",
2150
+ * // timestamp: expect.any(Number),
2151
+ * // deps: [],
2152
+ * // lamport: 0,
2153
+ * // msg: undefined,
2154
+ * // ops: [{
2155
+ * // container: "cid:root-text:Text",
2156
+ * // counter: 0,
2157
+ * // content: {
2158
+ * // type: "insert",
2159
+ * // pos: 0,
2160
+ * // text: "Hello"
2161
+ * // }
2162
+ * // }]
2163
+ * // }]
2164
+ * });
2165
+ * docB.import(snapshot);
2275
2166
  * ```
2276
2167
  */
2277
- importUpdateBatch(data: Uint8Array[]): ImportStatus;
2168
+ findIdSpansBetween(from: ({ peer: PeerID, counter: number })[], to: ({ peer: PeerID, counter: number })[]): VersionVectorDiff;
2278
2169
  /**
2279
- * Import a batch of updates or snapshots.
2170
+ * Get the change of with specific peer_id and lamport <= given lamport
2171
+ */
2172
+ getChangeAtLamport(peer_id: string, lamport: number): Change | undefined;
2173
+ /**
2174
+ * Get the path from the root to the container
2175
+ */
2176
+ getPathToContainer(id: ContainerID): (string|number)[] | undefined;
2177
+ /**
2178
+ * Gets container IDs modified in the given ID range.
2280
2179
  *
2281
- * It's more efficient than importing updates one by one.
2180
+ * **NOTE:** This method will implicitly commit.
2282
2181
  *
2283
- * @example
2284
- * ```ts
2285
- * import { LoroDoc } from "loro-crdt";
2182
+ * This method identifies which containers were affected by changes in a given range of operations.
2183
+ * It can be used together with `doc.travelChangeAncestors()` to analyze the history of changes
2184
+ * and determine which containers were modified by each change.
2286
2185
  *
2287
- * const doc = new LoroDoc();
2288
- * const text = doc.getText("text");
2289
- * text.insert(0, "Hello");
2290
- * const updates = doc.export({ mode: "update" });
2291
- * const snapshot = doc.export({ mode: "snapshot" });
2292
- * const doc2 = new LoroDoc();
2293
- * doc2.importBatch([snapshot, updates]);
2294
- * ```
2186
+ * @param id - The starting ID of the change range
2187
+ * @param len - The length of the change range to check
2188
+ * @returns An array of container IDs that were modified in the given range
2295
2189
  */
2296
- importBatch(data: Uint8Array[]): ImportStatus;
2190
+ getChangedContainersIn(id: { peer: PeerID, counter: number }, len: number): ContainerID[];
2297
2191
  /**
2298
- * Get the shallow json format of the document state.
2192
+ * Get deep value of the document with container id
2193
+ */
2194
+ getDeepValueWithID(): any;
2195
+ /**
2196
+ * Set the origin of the next commit
2197
+ */
2198
+ setNextCommitOrigin(origin: string): void;
2199
+ /**
2200
+ * Get the pending operations from the current transaction in JSON format
2299
2201
  *
2300
- * Unlike `toJSON()` which recursively resolves all containers to their values,
2301
- * `getShallowValue()` returns container IDs as strings for any nested containers.
2202
+ * This method returns a JSON representation of operations that have been applied
2203
+ * but not yet committed in the current transaction.
2204
+ *
2205
+ * It will use the same data format as `doc.exportJsonUpdates()`
2302
2206
  *
2303
2207
  * @example
2304
2208
  * ```ts
2305
- * import { LoroDoc } from "loro-crdt";
2306
- *
2307
2209
  * const doc = new LoroDoc();
2308
- * const list = doc.getList("list");
2309
- * const tree = doc.getTree("tree");
2310
- * const map = doc.getMap("map");
2311
- * const shallowValue = doc.getShallowValue();
2312
- * console.log(shallowValue);
2313
- * // {
2314
- * // list: 'cid:root-list:List',
2315
- * // tree: 'cid:root-tree:Tree',
2316
- * // map: 'cid:root-map:Map'
2317
- * // }
2318
- *
2319
- * // It points to the same container as `list`
2320
- * const listB = doc.getContainerById(shallowValue.list);
2210
+ * const text = doc.getText("text");
2211
+ * text.insert(0, "Hello");
2212
+ * // Get pending ops before commit
2213
+ * const pendingOps = doc.getPendingOpsFromCurrentTxnAsJson();
2214
+ * doc.commit();
2215
+ * const emptyOps = doc.getPendingOpsFromCurrentTxnAsJson(); // this is undefined
2321
2216
  * ```
2322
2217
  */
2323
- getShallowValue(): Record<string, ContainerID>;
2218
+ getUncommittedOpsAsJson(): JsonSchema | undefined;
2324
2219
  /**
2325
- * Get the json format of the entire document state.
2326
- *
2327
- * Unlike `getShallowValue()` which returns container IDs as strings,
2328
- * `toJSON()` recursively resolves all containers to their actual values.
2220
+ * Set the commit message of the next commit
2221
+ */
2222
+ setNextCommitMessage(msg: string): void;
2223
+ /**
2224
+ * Set the options of the next commit
2225
+ */
2226
+ setNextCommitOptions(options: { origin?: string, timestamp?: number, message?: string }): void;
2227
+ /**
2228
+ * The doc only contains the history since this version
2329
2229
  *
2330
- * @example
2331
- * ```ts
2332
- * import { LoroDoc, LoroText, LoroMap } from "loro-crdt";
2230
+ * This is empty if the doc is not shallow.
2333
2231
  *
2334
- * const doc = new LoroDoc();
2335
- * const list = doc.getList("list");
2336
- * list.insert(0, "Hello");
2337
- * const text = list.insertContainer(0, new LoroText());
2338
- * text.insert(0, "Hello");
2339
- * const map = list.insertContainer(1, new LoroMap());
2340
- * map.set("foo", "bar");
2341
- * console.log(doc.toJSON());
2342
- * // {"list": ["Hello", {"foo": "bar"}]}
2343
- * ```
2232
+ * The ops included by the shallow history start frontiers are not in the doc.
2344
2233
  */
2345
- toJSON(): any;
2234
+ shallowSinceFrontiers(): { peer: PeerID, counter: number }[];
2346
2235
  /**
2347
- * Debug the size of the history
2236
+ * Visit all the ancestors of the changes in causal order.
2237
+ *
2238
+ * @param ids - the changes to visit
2239
+ * @param f - the callback function, return `true` to continue visiting, return `false` to stop
2348
2240
  */
2349
- debugHistory(): void;
2241
+ travelChangeAncestors(ids: ({ peer: PeerID, counter: number })[], f: (change: Change) => boolean): void;
2350
2242
  /**
2351
- * Get the number of changes in the oplog.
2243
+ * Clear the options of the next commit
2352
2244
  */
2353
- changeCount(): number;
2245
+ clearNextCommitOptions(): void;
2354
2246
  /**
2355
- * Get the number of ops in the oplog.
2247
+ * Configures the default text style for the document.
2248
+ *
2249
+ * This method sets the default text style configuration for the document when using LoroText.
2250
+ * If `None` is provided, the default style is reset.
2356
2251
  */
2357
- opCount(): number;
2252
+ configDefaultTextStyle(style: { expand: 'before'|'after'|'none'|'both' } | undefined): void;
2358
2253
  /**
2359
- * Get all of changes in the oplog.
2254
+ * If two continuous local changes are within (<=) the interval(**in seconds**), they will be merged into one change.
2360
2255
  *
2361
- * Note: this method is expensive when the oplog is large. O(n)
2256
+ * The default value is 1_000 seconds.
2257
+ *
2258
+ * By default, we record timestamps in seconds for each change. So if the merge interval is 1, and changes A and B
2259
+ * have timestamps of 3 and 4 respectively, then they will be merged into one change
2260
+ */
2261
+ setChangeMergeInterval(interval: number): void;
2262
+ /**
2263
+ * Set the timestamp of the next commit
2264
+ */
2265
+ setNextCommitTimestamp(timestamp: number): void;
2266
+ /**
2267
+ * Set whether to hide empty root containers.
2362
2268
  *
2363
2269
  * @example
2364
2270
  * ```ts
2365
- * import { LoroDoc, LoroText } from "loro-crdt";
2366
- *
2367
2271
  * const doc = new LoroDoc();
2368
- * const text = doc.getText("text");
2369
- * text.insert(0, "Hello");
2370
- * const changes = doc.getAllChanges();
2371
- *
2372
- * for (let [peer, c] of changes.entries()){
2373
- * console.log("peer: ", peer);
2374
- * for (let change of c){
2375
- * console.log("change: ", change);
2376
- * }
2377
- * }
2272
+ * const map = doc.getMap("map");
2273
+ * console.log(doc.toJSON()); // { map: {} }
2274
+ * doc.setHideEmptyRootContainers(true);
2275
+ * console.log(doc.toJSON()); // {}
2378
2276
  * ```
2379
2277
  */
2380
- getAllChanges(): Map<PeerID, Change[]>;
2278
+ setHideEmptyRootContainers(hide: boolean): void;
2381
2279
  /**
2382
- * Get the change that contains the specific ID
2280
+ * Whether the editing is enabled in detached mode.
2281
+ *
2282
+ * The doc enter detached mode after calling `detach` or checking out a non-latest version.
2283
+ *
2284
+ * # Important Notes:
2285
+ *
2286
+ * - This mode uses a different PeerID for each checkout.
2287
+ * - Ensure no concurrent operations share the same PeerID if set manually.
2288
+ * - Importing does not affect the document's state or version; changes are
2289
+ * recorded in the [OpLog] only. Call `checkout` to apply changes.
2383
2290
  */
2384
- getChangeAt(id: { peer: PeerID, counter: number }): Change;
2291
+ isDetachedEditingEnabled(): boolean;
2385
2292
  /**
2386
- * Get the change of with specific peer_id and lamport <= given lamport
2293
+ * Create a new loro document.
2294
+ *
2295
+ * New document will have a random peer id.
2387
2296
  */
2388
- getChangeAtLamport(peer_id: string, lamport: number): Change | undefined;
2297
+ constructor();
2389
2298
  /**
2390
- * Get all ops of the change that contains the specific ID
2299
+ * Duplicate the document with a different PeerID
2300
+ *
2301
+ * The time complexity and space complexity of this operation are both O(n),
2302
+ *
2303
+ * When called in detached mode, it will fork at the current state frontiers.
2304
+ * It will have the same effect as `forkAt(&self.frontiers())`.
2391
2305
  */
2392
- getOpsInChange(id: { peer: PeerID, counter: number }): any[];
2306
+ fork(): LoroDoc;
2393
2307
  /**
2394
- * Convert frontiers to a version vector
2308
+ * Attach the document state to the latest known version.
2395
2309
  *
2396
- * Learn more about frontiers and version vector [here](https://loro.dev/docs/advanced/version_deep_dive)
2310
+ * > The document becomes detached during a `checkout` operation.
2311
+ * > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
2312
+ * > In a detached state, the document is not editable, and any `import` operations will be
2313
+ * > recorded in the `OpLog` without being applied to the `DocState`.
2314
+ *
2315
+ * This method has the same effect as invoking `checkoutToLatest`.
2397
2316
  *
2398
2317
  * @example
2399
2318
  * ```ts
@@ -2401,189 +2320,242 @@ export class LoroDoc {
2401
2320
  *
2402
2321
  * const doc = new LoroDoc();
2403
2322
  * const text = doc.getText("text");
2404
- * text.insert(0, "Hello");
2405
2323
  * const frontiers = doc.frontiers();
2406
- * const version = doc.frontiersToVV(frontiers);
2324
+ * text.insert(0, "Hello World!");
2325
+ * doc.checkout(frontiers);
2326
+ * // you need call `attach()` or `checkoutToLatest()` before changing the doc.
2327
+ * doc.attach();
2328
+ * text.insert(0, "Hi");
2407
2329
  * ```
2408
2330
  */
2409
- frontiersToVV(frontiers: ({ peer: PeerID, counter: number })[]): VersionVector;
2331
+ attach(): void;
2410
2332
  /**
2411
- * Convert a version vector to frontiers
2333
+ * Commit the cumulative auto-committed transaction.
2334
+ *
2335
+ * You can specify the `origin`, `timestamp`, and `message` of the commit.
2336
+ *
2337
+ * - The `origin` is used to mark the event
2338
+ * - The `message` works like a git commit message, which will be recorded and synced to peers
2339
+ * - The `timestamp` is the number of seconds that have elapsed since 00:00:00 UTC on January 1, 1970.
2340
+ * It defaults to `Date.now() / 1000` when timestamp recording is enabled
2341
+ *
2342
+ * The events will be emitted after a transaction is committed. A transaction is committed when:
2343
+ *
2344
+ * - `doc.commit()` is called.
2345
+ * - `doc.export(mode)` is called.
2346
+ * - `doc.import(data)` is called.
2347
+ * - `doc.checkout(version)` is called.
2348
+ *
2349
+ * NOTE: Timestamps are forced to be in ascending order.
2350
+ * If you commit a new change with a timestamp that is less than the existing one,
2351
+ * the largest existing timestamp will be used instead.
2352
+ *
2353
+ * NOTE: The `origin` will not be persisted, but the `message` will.
2354
+ *
2355
+ * Behavior on empty commits:
2356
+ * - This method is an explicit commit. If the pending transaction is empty, any provided
2357
+ * options (message/timestamp/origin) are swallowed and will not carry over to the next commit.
2358
+ * - Implicit commits triggered by `export`/`checkout` act as processing barriers. If the
2359
+ * transaction is empty in those cases, `message`/`timestamp`/`origin` are preserved for the
2360
+ * next commit.
2361
+ */
2362
+ commit(options?: { origin?: string, timestamp?: number, message?: string } | null): void;
2363
+ /**
2364
+ * Detach the document state from the latest known version.
2365
+ *
2366
+ * After detaching, all import operations will be recorded in the `OpLog` without being applied to the `DocState`.
2367
+ * When `detached`, the document is not editable.
2412
2368
  *
2413
2369
  * @example
2414
2370
  * ```ts
2415
2371
  * import { LoroDoc } from "loro-crdt";
2416
2372
  *
2417
2373
  * const doc = new LoroDoc();
2418
- * const text = doc.getText("text");
2419
- * text.insert(0, "Hello");
2420
- * const version = doc.version();
2421
- * const frontiers = doc.vvToFrontiers(version);
2374
+ * doc.detach();
2375
+ * console.log(doc.isDetached()); // true
2422
2376
  * ```
2423
2377
  */
2424
- vvToFrontiers(vv: VersionVector): { peer: PeerID, counter: number }[];
2378
+ detach(): void;
2425
2379
  /**
2426
- * Get the value or container at the given path
2427
- *
2428
- * The path can be specified in different ways depending on the container type:
2429
- *
2430
- * For Tree:
2431
- * 1. Using node IDs: `tree/{node_id}/property`
2432
- * 2. Using indices: `tree/0/1/property`
2433
- *
2434
- * For List and MovableList:
2435
- * - Using indices: `list/0` or `list/1/property`
2380
+ * Export the document based on the specified ExportMode.
2436
2381
  *
2437
- * For Map:
2438
- * - Using keys: `map/key` or `map/nested/property`
2382
+ * @param mode - The export mode to use. Can be one of:
2383
+ * - `{ mode: "snapshot" }`: Export a full snapshot of the document.
2384
+ * - `{ mode: "update", from?: VersionVector }`: Export updates from the given version vector.
2385
+ * If `from` is not provided, it will export the whole history of the document.
2386
+ * - `{ mode: "updates-in-range", spans: { id: ID, len: number }[] }`: Export updates within the specified ID spans.
2387
+ * - `{ mode: "shallow-snapshot", frontiers: Frontiers }`: Export a garbage-collected snapshot up to the given frontiers.
2439
2388
  *
2440
- * For tree structures, index-based paths follow depth-first traversal order.
2441
- * The indices start from 0 and represent the position of a node among its siblings.
2389
+ * @returns A byte array containing the exported data.
2442
2390
  *
2443
2391
  * @example
2444
2392
  * ```ts
2445
- * import { LoroDoc } from "loro-crdt";
2393
+ * import { LoroDoc, LoroText } from "loro-crdt";
2446
2394
  *
2447
2395
  * const doc = new LoroDoc();
2448
- * const map = doc.getMap("map");
2449
- * map.set("key", 1);
2450
- * console.log(doc.getByPath("map/key")); // 1
2451
- * console.log(doc.getByPath("map")); // LoroMap
2396
+ * doc.setPeerId("1");
2397
+ * doc.getText("text").update("Hello World");
2398
+ *
2399
+ * // Export a full snapshot
2400
+ * const snapshotBytes = doc.export({ mode: "snapshot" });
2401
+ *
2402
+ * // Export updates from a specific version
2403
+ * const vv = doc.oplogVersion();
2404
+ * doc.getText("text").update("Hello Loro");
2405
+ * const updateBytes = doc.export({ mode: "update", from: vv });
2406
+ *
2407
+ * // Export a shallow snapshot that only includes the history since the frontiers
2408
+ * const shallowBytes = doc.export({ mode: "shallow-snapshot", frontiers: doc.oplogFrontiers() });
2409
+ *
2410
+ * // Export updates within specific ID spans
2411
+ * const spanBytes = doc.export({
2412
+ * mode: "updates-in-range",
2413
+ * spans: [{ id: { peer: "1", counter: 0 }, len: 10 }]
2414
+ * });
2452
2415
  * ```
2453
2416
  */
2454
- getByPath(path: string): Value | Container | undefined;
2417
+ export(mode: ExportMode): Uint8Array;
2455
2418
  /**
2456
- * Get the absolute position of the given Cursor
2419
+ * Import snapshot or updates into current doc.
2420
+ *
2421
+ * Note:
2422
+ * - Updates within the current version will be ignored
2423
+ * - Updates with missing dependencies will be pending until the dependencies are received
2457
2424
  *
2458
2425
  * @example
2459
2426
  * ```ts
2427
+ * import { LoroDoc } from "loro-crdt";
2428
+ *
2460
2429
  * const doc = new LoroDoc();
2461
2430
  * const text = doc.getText("text");
2462
- * text.insert(0, "123");
2463
- * const pos0 = text.getCursor(0, 0);
2464
- * {
2465
- * const ans = doc.getCursorPos(pos0!);
2466
- * expect(ans.offset).toBe(0);
2467
- * }
2468
- * text.insert(0, "1");
2469
- * {
2470
- * const ans = doc.getCursorPos(pos0!);
2471
- * expect(ans.offset).toBe(1);
2472
- * }
2431
+ * text.insert(0, "Hello");
2432
+ * // get all updates of the doc
2433
+ * const updates = doc.export({ mode: "update" });
2434
+ * const snapshot = doc.export({ mode: "snapshot" });
2435
+ * const doc2 = new LoroDoc();
2436
+ * // import snapshot
2437
+ * doc2.import(snapshot);
2438
+ * // or import updates
2439
+ * doc2.import(updates);
2473
2440
  * ```
2474
2441
  */
2475
- getCursorPos(cursor: Cursor): { update?: Cursor, offset: number, side: Side } | undefined;
2442
+ import(update_or_snapshot: Uint8Array): ImportStatus;
2476
2443
  /**
2477
- * Gets container IDs modified in the given ID range.
2478
- *
2479
- * **NOTE:** This method will implicitly commit.
2480
- *
2481
- * This method identifies which containers were affected by changes in a given range of operations.
2482
- * It can be used together with `doc.travelChangeAncestors()` to analyze the history of changes
2483
- * and determine which containers were modified by each change.
2444
+ * Creates a new LoroDoc at a specified version (Frontiers)
2484
2445
  *
2485
- * @param id - The starting ID of the change range
2486
- * @param len - The length of the change range to check
2487
- * @returns An array of container IDs that were modified in the given range
2446
+ * The created doc will only contain the history before the specified frontiers.
2488
2447
  */
2489
- getChangedContainersIn(id: { peer: PeerID, counter: number }, len: number): ContainerID[];
2448
+ forkAt(frontiers: ({ peer: PeerID, counter: number })[]): LoroDoc;
2490
2449
  /**
2491
- * Revert the document to the given frontiers.
2450
+ * Get the number of ops in the oplog.
2451
+ */
2452
+ opCount(): number;
2453
+ /**
2454
+ * Get the json format of the entire document state.
2492
2455
  *
2493
- * The doc will not become detached when using this method. Instead, it will generate a series
2494
- * of operations to revert the document to the given version.
2456
+ * Unlike `getShallowValue()` which returns container IDs as strings,
2457
+ * `toJSON()` recursively resolves all containers to their actual values.
2495
2458
  *
2496
2459
  * @example
2497
2460
  * ```ts
2461
+ * import { LoroDoc, LoroText, LoroMap } from "loro-crdt";
2462
+ *
2498
2463
  * const doc = new LoroDoc();
2499
- * doc.setPeerId("1");
2500
- * const text = doc.getText("text");
2464
+ * const list = doc.getList("list");
2465
+ * list.insert(0, "Hello");
2466
+ * const text = list.insertContainer(0, new LoroText());
2501
2467
  * text.insert(0, "Hello");
2502
- * doc.commit();
2503
- * doc.revertTo([{ peer: "1", counter: 1 }]);
2504
- * expect(doc.getText("text").toString()).toBe("He");
2468
+ * const map = list.insertContainer(1, new LoroMap());
2469
+ * map.set("foo", "bar");
2470
+ * console.log(doc.toJSON());
2471
+ * // {"list": ["Hello", {"foo": "bar"}]}
2505
2472
  * ```
2506
2473
  */
2507
- revertTo(frontiers: ({ peer: PeerID, counter: number })[]): void;
2474
+ toJSON(): any;
2508
2475
  /**
2509
- * Apply a batch of diff to the document
2476
+ * Get the version vector of the current document state.
2510
2477
  *
2511
- * A diff batch represents a set of changes between two versions of the document.
2512
- * You can calculate a diff batch using `doc.diff()`.
2478
+ * If you checkout to a specific version, the version vector will change.
2479
+ */
2480
+ version(): VersionVector;
2481
+ /**
2482
+ * Checkout the `DocState` to a specific version.
2513
2483
  *
2514
- * Changes are associated with container IDs. During diff application, if new containers were created in the source
2515
- * document, they will be assigned fresh IDs in the target document. Loro automatically handles remapping these
2516
- * container IDs from their original IDs to the new IDs as the diff is applied.
2484
+ * > The document becomes detached during a `checkout` operation.
2485
+ * > Being `detached` implies that the `DocState` is not synchronized with the latest version of the `OpLog`.
2486
+ * > In a detached state, the document is not editable, and any `import` operations will be
2487
+ * > recorded in the `OpLog` without being applied to the `DocState`.
2517
2488
  *
2518
- * @example
2519
- * ```ts
2520
- * const doc1 = new LoroDoc();
2521
- * const doc2 = new LoroDoc();
2489
+ * You should call `attach` to attach the `DocState` to the latest version of `OpLog`.
2522
2490
  *
2523
- * // Make some changes to doc1
2524
- * const text = doc1.getText("text");
2525
- * text.insert(0, "Hello");
2491
+ * @param frontiers - the specific frontiers
2526
2492
  *
2527
- * // Calculate diff between empty and current state
2528
- * const diff = doc1.diff([], doc1.frontiers());
2493
+ * @example
2494
+ * ```ts
2495
+ * import { LoroDoc } from "loro-crdt";
2529
2496
  *
2530
- * // Apply changes to doc2
2531
- * doc2.applyDiff(diff);
2532
- * console.log(doc2.getText("text").toString()); // "Hello"
2497
+ * const doc = new LoroDoc();
2498
+ * const text = doc.getText("text");
2499
+ * const frontiers = doc.frontiers();
2500
+ * text.insert(0, "Hello World!");
2501
+ * doc.checkout(frontiers);
2502
+ * console.log(doc.toJSON()); // {"text": ""}
2533
2503
  * ```
2534
2504
  */
2535
- applyDiff(diff: [ContainerID, Diff|JsonDiff][]): void;
2505
+ checkout(frontiers: ({ peer: PeerID, counter: number })[]): void;
2536
2506
  /**
2537
- * Get the pending operations from the current transaction in JSON format
2507
+ * Get a LoroText by container id.
2538
2508
  *
2539
- * This method returns a JSON representation of operations that have been applied
2540
- * but not yet committed in the current transaction.
2509
+ * The object returned is a new js object each time because it need to cross
2510
+ * the WASM boundary.
2541
2511
  *
2542
- * It will use the same data format as `doc.exportJsonUpdates()`
2512
+ * If the container does not exist, an error will be thrown.
2543
2513
  *
2544
2514
  * @example
2545
2515
  * ```ts
2516
+ * import { LoroDoc } from "loro-crdt";
2517
+ *
2546
2518
  * const doc = new LoroDoc();
2547
2519
  * const text = doc.getText("text");
2548
- * text.insert(0, "Hello");
2549
- * // Get pending ops before commit
2550
- * const pendingOps = doc.getPendingOpsFromCurrentTxnAsJson();
2551
- * doc.commit();
2552
- * const emptyOps = doc.getPendingOpsFromCurrentTxnAsJson(); // this is undefined
2553
2520
  * ```
2554
2521
  */
2555
- getUncommittedOpsAsJson(): JsonSchema | undefined;
2522
+ getText(cid: ContainerID | string): LoroText;
2556
2523
  /**
2557
- * Delete all content from a root container and hide it from the document.
2558
- *
2559
- * When a root container is empty and hidden:
2560
- * - It won't show up in `get_deep_value()` results
2561
- * - It won't be included in document snapshots
2524
+ * Get the [frontiers](https://loro.dev/docs/advanced/version_deep_dive) of the current document state.
2562
2525
  *
2563
- * Only works on root containers (containers without parents).
2526
+ * If you checkout to a specific version, this value will change.
2564
2527
  */
2565
- deleteRootContainer(cid: ContainerID): void;
2528
+ frontiers(): { peer: PeerID, counter: number }[];
2566
2529
  /**
2567
- * Set whether to hide empty root containers.
2530
+ * Evaluate JSONPath against a LoroDoc
2531
+ */
2532
+ JSONPath(jsonpath: string): Array<any>;
2533
+ /**
2534
+ * Revert the document to the given frontiers.
2535
+ *
2536
+ * The doc will not become detached when using this method. Instead, it will generate a series
2537
+ * of operations to revert the document to the given version.
2568
2538
  *
2569
2539
  * @example
2570
2540
  * ```ts
2571
2541
  * const doc = new LoroDoc();
2572
- * const map = doc.getMap("map");
2573
- * console.log(doc.toJSON()); // { map: {} }
2574
- * doc.setHideEmptyRootContainers(true);
2575
- * console.log(doc.toJSON()); // {}
2542
+ * doc.setPeerId("1");
2543
+ * const text = doc.getText("text");
2544
+ * text.insert(0, "Hello");
2545
+ * doc.commit();
2546
+ * doc.revertTo([{ peer: "1", counter: 1 }]);
2547
+ * expect(doc.getText("text").toString()).toBe("He");
2576
2548
  * ```
2577
2549
  */
2578
- setHideEmptyRootContainers(hide: boolean): void;
2579
- /**
2580
- * Peer ID of the current writer.
2581
- */
2582
- readonly peerId: bigint;
2550
+ revertTo(frontiers: ({ peer: PeerID, counter: number })[]): void;
2583
2551
  /**
2584
2552
  * Get peer id in decimal string.
2585
2553
  */
2586
2554
  readonly peerIdStr: PeerID;
2555
+ /**
2556
+ * Peer ID of the current writer.
2557
+ */
2558
+ readonly peerId: bigint;
2587
2559
  }
2588
2560
  /**
2589
2561
  * The handler of a list container.
@@ -2592,6 +2564,37 @@ export class LoroDoc {
2592
2564
  */
2593
2565
  export class LoroList {
2594
2566
  free(): void;
2567
+ /**
2568
+ * Whether the container is attached to a document.
2569
+ *
2570
+ * If it's detached, the operations on the container will not be persisted.
2571
+ */
2572
+ isAttached(): boolean;
2573
+ /**
2574
+ * Get the attached container associated with this.
2575
+ *
2576
+ * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
2577
+ */
2578
+ getAttached(): LoroList | undefined;
2579
+ /**
2580
+ * Get the shallow value of the list.
2581
+ *
2582
+ * Unlike `toJSON()` which recursively resolves all containers to their values,
2583
+ * `getShallowValue()` returns container IDs as strings for any nested containers.
2584
+ *
2585
+ * ```js
2586
+ * const doc = new LoroDoc();
2587
+ * doc.setPeerId("1");
2588
+ * const list = doc.getList("list");
2589
+ * list.insert(0, 1);
2590
+ * list.insert(1, "two");
2591
+ * const subList = list.insertContainer(2, new LoroList());
2592
+ * subList.insert(0, "sub");
2593
+ * list.getShallowValue(); // [1, "two", "cid:2@1:List"]
2594
+ * list.toJSON(); // [1, "two", ["sub"]]
2595
+ * ```
2596
+ */
2597
+ getShallowValue(): Value[];
2595
2598
  /**
2596
2599
  * Create a new detached LoroList (not attached to any LoroDoc).
2597
2600
  *
@@ -2599,10 +2602,18 @@ export class LoroList {
2599
2602
  * To attach the container to the document, please insert it into an attached container.
2600
2603
  */
2601
2604
  constructor();
2605
+ /**
2606
+ * Pop a value from the end of the list.
2607
+ */
2608
+ pop(): Value | undefined;
2602
2609
  /**
2603
2610
  * "List"
2604
2611
  */
2605
2612
  kind(): 'List';
2613
+ /**
2614
+ * Delete all elements in the list.
2615
+ */
2616
+ clear(): void;
2606
2617
  /**
2607
2618
  * Delete elements from index to index + len.
2608
2619
  *
@@ -2618,6 +2629,15 @@ export class LoroList {
2618
2629
  * ```
2619
2630
  */
2620
2631
  delete(index: number, len: number): void;
2632
+ /**
2633
+ * Get the parent container.
2634
+ *
2635
+ * - The parent container of the root tree is `undefined`.
2636
+ * - The object returned is a new js object each time because it need to cross
2637
+ * the WASM boundary.
2638
+ */
2639
+ parent(): Container | undefined;
2640
+ getIdAt(pos: number): { peer: PeerID, counter: number } | undefined;
2621
2641
  /**
2622
2642
  * Get elements of the list. If the type of a element is a container, it will be
2623
2643
  * resolved recursively.
@@ -2635,58 +2655,10 @@ export class LoroList {
2635
2655
  * ```
2636
2656
  */
2637
2657
  toJSON(): any;
2638
- /**
2639
- * Get the parent container.
2640
- *
2641
- * - The parent container of the root tree is `undefined`.
2642
- * - The object returned is a new js object each time because it need to cross
2643
- * the WASM boundary.
2644
- */
2645
- parent(): Container | undefined;
2646
- /**
2647
- * Whether the container is attached to a document.
2648
- *
2649
- * If it's detached, the operations on the container will not be persisted.
2650
- */
2651
- isAttached(): boolean;
2652
- /**
2653
- * Get the attached container associated with this.
2654
- *
2655
- * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
2656
- */
2657
- getAttached(): LoroList | undefined;
2658
- /**
2659
- * Pop a value from the end of the list.
2660
- */
2661
- pop(): Value | undefined;
2662
- /**
2663
- * Delete all elements in the list.
2664
- */
2665
- clear(): void;
2666
- getIdAt(pos: number): { peer: PeerID, counter: number } | undefined;
2667
2658
  /**
2668
2659
  * Check if the container is deleted
2669
2660
  */
2670
2661
  isDeleted(): boolean;
2671
- /**
2672
- * Get the shallow value of the list.
2673
- *
2674
- * Unlike `toJSON()` which recursively resolves all containers to their values,
2675
- * `getShallowValue()` returns container IDs as strings for any nested containers.
2676
- *
2677
- * ```js
2678
- * const doc = new LoroDoc();
2679
- * doc.setPeerId("1");
2680
- * const list = doc.getList("list");
2681
- * list.insert(0, 1);
2682
- * list.insert(1, "two");
2683
- * const subList = list.insertContainer(2, new LoroList());
2684
- * subList.insert(0, "sub");
2685
- * list.getShallowValue(); // [1, "two", "cid:2@1:List"]
2686
- * list.toJSON(); // [1, "two", ["sub"]]
2687
- * ```
2688
- */
2689
- getShallowValue(): Value[];
2690
2662
  /**
2691
2663
  * Get the id of this container.
2692
2664
  */
@@ -2716,30 +2688,55 @@ export class LoroList {
2716
2688
  export class LoroMap {
2717
2689
  free(): void;
2718
2690
  /**
2719
- * Create a new detached LoroMap (not attached to any LoroDoc).
2691
+ * Whether the container is attached to a document.
2720
2692
  *
2721
- * The edits on a detached container will not be persisted.
2722
- * To attach the container to the document, please insert it into an attached container.
2693
+ * If it's detached, the operations on the container will not be persisted.
2723
2694
  */
2724
- constructor();
2695
+ isAttached(): boolean;
2725
2696
  /**
2726
- * "Map"
2697
+ * Get the attached container associated with this.
2698
+ *
2699
+ * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
2727
2700
  */
2728
- kind(): 'Map';
2701
+ getAttached(): LoroMap | undefined;
2729
2702
  /**
2730
- * Remove the key from the map.
2703
+ * Get the peer id of the last editor on the given entry
2704
+ */
2705
+ getLastEditor(key: string): PeerID | undefined;
2706
+ /**
2707
+ * Get the shallow value of the map.
2708
+ *
2709
+ * Unlike `toJSON()` which recursively resolves all containers to their values,
2710
+ * `getShallowValue()` returns container IDs as strings for any nested containers.
2731
2711
  *
2732
2712
  * @example
2733
2713
  * ```ts
2734
- * import { LoroDoc } from "loro-crdt";
2714
+ * import { LoroDoc, LoroText } from "loro-crdt";
2735
2715
  *
2736
2716
  * const doc = new LoroDoc();
2717
+ * doc.setPeerId("1");
2737
2718
  * const map = doc.getMap("map");
2738
- * map.set("foo", "bar");
2739
- * map.delete("foo");
2719
+ * map.set("key", "value");
2720
+ * const subText = map.setContainer("text", new LoroText());
2721
+ * subText.insert(0, "Hello");
2722
+ *
2723
+ * // Get shallow value - nested containers are represented by their IDs
2724
+ * console.log(map.getShallowValue());
2725
+ * // Output: { key: "value", text: "cid:1@1:Text" }
2726
+ *
2727
+ * // Get full value with nested containers resolved by `toJSON()`
2728
+ * console.log(map.toJSON());
2729
+ * // Output: { key: "value", text: "Hello" }
2740
2730
  * ```
2741
2731
  */
2742
- delete(key: string): void;
2732
+ getShallowValue(): Record<string, Value>;
2733
+ /**
2734
+ * Create a new detached LoroMap (not attached to any LoroDoc).
2735
+ *
2736
+ * The edits on a detached container will not be persisted.
2737
+ * To attach the container to the document, please insert it into an attached container.
2738
+ */
2739
+ constructor();
2743
2740
  /**
2744
2741
  * Get the keys of the map.
2745
2742
  *
@@ -2755,6 +2752,36 @@ export class LoroMap {
2755
2752
  * ```
2756
2753
  */
2757
2754
  keys(): any[];
2755
+ /**
2756
+ * "Map"
2757
+ */
2758
+ kind(): 'Map';
2759
+ /**
2760
+ * Delete all key-value pairs in the map.
2761
+ */
2762
+ clear(): void;
2763
+ /**
2764
+ * Remove the key from the map.
2765
+ *
2766
+ * @example
2767
+ * ```ts
2768
+ * import { LoroDoc } from "loro-crdt";
2769
+ *
2770
+ * const doc = new LoroDoc();
2771
+ * const map = doc.getMap("map");
2772
+ * map.set("foo", "bar");
2773
+ * map.delete("foo");
2774
+ * ```
2775
+ */
2776
+ delete(key: string): void;
2777
+ /**
2778
+ * Get the parent container.
2779
+ *
2780
+ * - The parent container of the root tree is `undefined`.
2781
+ * - The object returned is a new js object each time because it need to cross
2782
+ * the WASM boundary.
2783
+ */
2784
+ parent(): Container | undefined;
2758
2785
  /**
2759
2786
  * Get the values of the map. If the value is a child container, the corresponding
2760
2787
  * `Container` will be returned.
@@ -2803,66 +2830,11 @@ export class LoroMap {
2803
2830
  * console.log(map.toJSON()); // {"foo": "bar", "text": "Hello"}
2804
2831
  * ```
2805
2832
  */
2806
- toJSON(): any;
2807
- /**
2808
- * Get the parent container.
2809
- *
2810
- * - The parent container of the root tree is `undefined`.
2811
- * - The object returned is a new js object each time because it need to cross
2812
- * the WASM boundary.
2813
- */
2814
- parent(): Container | undefined;
2815
- /**
2816
- * Whether the container is attached to a document.
2817
- *
2818
- * If it's detached, the operations on the container will not be persisted.
2819
- */
2820
- isAttached(): boolean;
2821
- /**
2822
- * Get the attached container associated with this.
2823
- *
2824
- * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
2825
- */
2826
- getAttached(): LoroMap | undefined;
2827
- /**
2828
- * Delete all key-value pairs in the map.
2829
- */
2830
- clear(): void;
2831
- /**
2832
- * Get the peer id of the last editor on the given entry
2833
- */
2834
- getLastEditor(key: string): PeerID | undefined;
2835
- /**
2836
- * Check if the container is deleted
2837
- */
2838
- isDeleted(): boolean;
2839
- /**
2840
- * Get the shallow value of the map.
2841
- *
2842
- * Unlike `toJSON()` which recursively resolves all containers to their values,
2843
- * `getShallowValue()` returns container IDs as strings for any nested containers.
2844
- *
2845
- * @example
2846
- * ```ts
2847
- * import { LoroDoc, LoroText } from "loro-crdt";
2848
- *
2849
- * const doc = new LoroDoc();
2850
- * doc.setPeerId("1");
2851
- * const map = doc.getMap("map");
2852
- * map.set("key", "value");
2853
- * const subText = map.setContainer("text", new LoroText());
2854
- * subText.insert(0, "Hello");
2855
- *
2856
- * // Get shallow value - nested containers are represented by their IDs
2857
- * console.log(map.getShallowValue());
2858
- * // Output: { key: "value", text: "cid:1@1:Text" }
2859
- *
2860
- * // Get full value with nested containers resolved by `toJSON()`
2861
- * console.log(map.toJSON());
2862
- * // Output: { key: "value", text: "Hello" }
2863
- * ```
2864
- */
2865
- getShallowValue(): Record<string, Value>;
2833
+ toJSON(): any;
2834
+ /**
2835
+ * Check if the container is deleted
2836
+ */
2837
+ isDeleted(): boolean;
2866
2838
  /**
2867
2839
  * The container id of this handler.
2868
2840
  */
@@ -2890,68 +2862,48 @@ export class LoroMap {
2890
2862
  export class LoroMovableList {
2891
2863
  free(): void;
2892
2864
  /**
2893
- * Create a new detached LoroMovableList (not attached to any LoroDoc).
2865
+ * Whether the container is attached to a document.
2894
2866
  *
2895
- * The edits on a detached container will not be persisted.
2896
- * To attach the container to the document, please insert it into an attached container.
2867
+ * If it's detached, the operations on the container will not be persisted.
2897
2868
  */
2898
- constructor();
2869
+ isAttached(): boolean;
2899
2870
  /**
2900
- * "MovableList"
2871
+ * Get the creator of the list item at the given position.
2901
2872
  */
2902
- kind(): 'MovableList';
2873
+ getCreatorAt(pos: number): PeerID | undefined;
2903
2874
  /**
2904
- * Delete elements from index to index + len.
2905
- *
2906
- * @example
2907
- * ```ts
2908
- * import { LoroDoc } from "loro-crdt";
2875
+ * Get the attached container associated with this.
2909
2876
  *
2910
- * const doc = new LoroDoc();
2911
- * const list = doc.getList("list");
2912
- * list.insert(0, 100);
2913
- * list.delete(0, 1);
2914
- * console.log(list.value); // []
2915
- * ```
2877
+ * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
2916
2878
  */
2917
- delete(index: number, len: number): void;
2879
+ getAttached(): LoroList | undefined;
2918
2880
  /**
2919
- * Get elements of the list. If the type of a element is a container, it will be
2920
- * resolved recursively.
2921
- *
2922
- * @example
2923
- * ```ts
2924
- * import { LoroDoc, LoroText } from "loro-crdt";
2925
- *
2926
- * const doc = new LoroDoc();
2927
- * const list = doc.getList("list");
2928
- * list.insert(0, 100);
2929
- * const text = list.insertContainer(1, new LoroText());
2930
- * text.insert(0, "Hello");
2931
- * console.log(list.toJSON()); // [100, "Hello"];
2932
- * ```
2881
+ * Get the last mover of the list item at the given position.
2933
2882
  */
2934
- toJSON(): any;
2883
+ getLastMoverAt(pos: number): PeerID | undefined;
2935
2884
  /**
2936
- * Get the parent container.
2937
- *
2938
- * - The parent container of the root tree is `undefined`.
2939
- * - The object returned is a new js object each time because it need to cross
2940
- * the WASM boundary.
2885
+ * Get the last editor of the list item at the given position.
2941
2886
  */
2942
- parent(): Container | undefined;
2887
+ getLastEditorAt(pos: number): PeerID | undefined;
2943
2888
  /**
2944
- * Whether the container is attached to a document.
2889
+ * Get the shallow value of the movable list.
2945
2890
  *
2946
- * If it's detached, the operations on the container will not be persisted.
2947
- */
2948
- isAttached(): boolean;
2949
- /**
2950
- * Get the attached container associated with this.
2891
+ * Unlike `toJSON()` which recursively resolves all containers to their values,
2892
+ * `getShallowValue()` returns container IDs as strings for any nested containers.
2951
2893
  *
2952
- * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
2894
+ * ```js
2895
+ * const doc = new LoroDoc();
2896
+ * doc.setPeerId("1");
2897
+ * const list = doc.getMovableList("list");
2898
+ * list.insert(0, 1);
2899
+ * list.insert(1, "two");
2900
+ * const subList = list.insertContainer(2, new LoroList());
2901
+ * subList.insert(0, "sub");
2902
+ * list.getShallowValue(); // [1, "two", "cid:2@1:List"]
2903
+ * list.toJSON(); // [1, "two", ["sub"]]
2904
+ * ```
2953
2905
  */
2954
- getAttached(): LoroList | undefined;
2906
+ getShallowValue(): Value[];
2955
2907
  /**
2956
2908
  * Move the element from `from` to `to`.
2957
2909
  *
@@ -2966,48 +2918,68 @@ export class LoroMovableList {
2966
2918
  */
2967
2919
  move(from: number, to: number): void;
2968
2920
  /**
2969
- * Pop a value from the end of the list.
2921
+ * Create a new detached LoroMovableList (not attached to any LoroDoc).
2922
+ *
2923
+ * The edits on a detached container will not be persisted.
2924
+ * To attach the container to the document, please insert it into an attached container.
2970
2925
  */
2971
- pop(): Value | undefined;
2926
+ constructor();
2972
2927
  /**
2973
- * Delete all elements in the list.
2928
+ * Pop a value from the end of the list.
2974
2929
  */
2975
- clear(): void;
2930
+ pop(): Value | undefined;
2976
2931
  /**
2977
- * Get the creator of the list item at the given position.
2932
+ * "MovableList"
2978
2933
  */
2979
- getCreatorAt(pos: number): PeerID | undefined;
2934
+ kind(): 'MovableList';
2980
2935
  /**
2981
- * Get the last mover of the list item at the given position.
2936
+ * Delete all elements in the list.
2982
2937
  */
2983
- getLastMoverAt(pos: number): PeerID | undefined;
2938
+ clear(): void;
2984
2939
  /**
2985
- * Get the last editor of the list item at the given position.
2940
+ * Delete elements from index to index + len.
2941
+ *
2942
+ * @example
2943
+ * ```ts
2944
+ * import { LoroDoc } from "loro-crdt";
2945
+ *
2946
+ * const doc = new LoroDoc();
2947
+ * const list = doc.getList("list");
2948
+ * list.insert(0, 100);
2949
+ * list.delete(0, 1);
2950
+ * console.log(list.value); // []
2951
+ * ```
2986
2952
  */
2987
- getLastEditorAt(pos: number): PeerID | undefined;
2953
+ delete(index: number, len: number): void;
2988
2954
  /**
2989
- * Check if the container is deleted
2955
+ * Get the parent container.
2956
+ *
2957
+ * - The parent container of the root tree is `undefined`.
2958
+ * - The object returned is a new js object each time because it need to cross
2959
+ * the WASM boundary.
2990
2960
  */
2991
- isDeleted(): boolean;
2961
+ parent(): Container | undefined;
2992
2962
  /**
2993
- * Get the shallow value of the movable list.
2963
+ * Get elements of the list. If the type of a element is a container, it will be
2964
+ * resolved recursively.
2994
2965
  *
2995
- * Unlike `toJSON()` which recursively resolves all containers to their values,
2996
- * `getShallowValue()` returns container IDs as strings for any nested containers.
2966
+ * @example
2967
+ * ```ts
2968
+ * import { LoroDoc, LoroText } from "loro-crdt";
2997
2969
  *
2998
- * ```js
2999
2970
  * const doc = new LoroDoc();
3000
- * doc.setPeerId("1");
3001
- * const list = doc.getMovableList("list");
3002
- * list.insert(0, 1);
3003
- * list.insert(1, "two");
3004
- * const subList = list.insertContainer(2, new LoroList());
3005
- * subList.insert(0, "sub");
3006
- * list.getShallowValue(); // [1, "two", "cid:2@1:List"]
3007
- * list.toJSON(); // [1, "two", ["sub"]]
2971
+ * const list = doc.getList("list");
2972
+ * list.insert(0, 100);
2973
+ * const text = list.insertContainer(1, new LoroText());
2974
+ * text.insert(0, "Hello");
2975
+ * console.log(list.toJSON()); // [100, "Hello"];
3008
2976
  * ```
3009
2977
  */
3010
- getShallowValue(): Value[];
2978
+ toJSON(): any;
2979
+ /**
2980
+ * Check if the container is deleted
2981
+ */
2982
+ isDeleted(): boolean;
3011
2983
  /**
3012
2984
  * Get the id of this container.
3013
2985
  */
@@ -3037,38 +3009,34 @@ export class LoroMovableList {
3037
3009
  export class LoroText {
3038
3010
  free(): void;
3039
3011
  /**
3040
- * Create a new detached LoroText (not attached to any LoroDoc).
3041
- *
3042
- * The edits on a detached container will not be persisted.
3043
- * To attach the container to the document, please insert it into an attached container.
3044
- */
3045
- constructor();
3046
- /**
3047
- * "Text"
3048
- */
3049
- kind(): 'Text';
3050
- /**
3051
- * Iterate each text span(internal storage unit)
3012
+ * Change the state of this text by delta.
3052
3013
  *
3053
- * The callback function will be called for each span in the text.
3054
- * If the callback returns `false`, the iteration will stop.
3014
+ * If a delta item is `insert`, it should include all the attributes of the inserted text.
3015
+ * Loro's rich text CRDT may make the inserted text inherit some styles when you use
3016
+ * `insert` method directly. However, when you use `applyDelta` if some attributes are
3017
+ * inherited from CRDT but not included in the delta, they will be removed.
3055
3018
  *
3056
- * Limitation: you cannot access or alter the doc state when iterating (this is for performance consideration).
3057
- * If you need to access or alter the doc state, please use `toString` instead.
3019
+ * Another special property of `applyDelta` is if you format an attribute for ranges out of
3020
+ * the text length, Loro will insert new lines to fill the gap first. It's useful when you
3021
+ * build the binding between Loro and rich text editors like Quill, which might assume there
3022
+ * is always a newline at the end of the text implicitly.
3058
3023
  *
3059
3024
  * @example
3060
3025
  * ```ts
3061
- * import { LoroDoc } from "loro-crdt";
3062
- *
3063
3026
  * const doc = new LoroDoc();
3064
3027
  * const text = doc.getText("text");
3065
- * text.insert(0, "Hello");
3066
- * text.iter((str) => (console.log(str), true));
3028
+ * doc.configTextStyle({bold: {expand: "after"}});
3029
+ * text.insert(0, "Hello World!");
3030
+ * text.mark({ start: 0, end: 5 }, "bold", true);
3031
+ * const delta = text.toDelta();
3032
+ * const text2 = doc.getText("text2");
3033
+ * text2.applyDelta(delta);
3034
+ * expect(text2.toDelta()).toStrictEqual(delta);
3067
3035
  * ```
3068
3036
  */
3069
- iter(callback: (string) => boolean): void;
3037
+ applyDelta(delta: Delta<string>[]): void;
3070
3038
  /**
3071
- * Insert the string at the given index (utf-16 index).
3039
+ * Delete elements from index to utf-8 index + len
3072
3040
  *
3073
3041
  * @example
3074
3042
  * ```ts
@@ -3076,12 +3044,19 @@ export class LoroText {
3076
3044
  *
3077
3045
  * const doc = new LoroDoc();
3078
3046
  * const text = doc.getText("text");
3079
- * text.insert(0, "Hello");
3047
+ * text.insertUtf8(0, "Hello");
3048
+ * text.deleteUtf8(1, 3);
3049
+ * const s = text.toString();
3050
+ * console.log(s); // "Ho"
3080
3051
  * ```
3081
3052
  */
3082
- insert(index: number, content: string): void;
3053
+ deleteUtf8(index: number, len: number): void;
3083
3054
  /**
3084
- * Get a string slice (utf-16 index).
3055
+ * Get the editor of the text at the given position.
3056
+ */
3057
+ getEditorOf(pos: number): PeerID | undefined;
3058
+ /**
3059
+ * Insert some string at utf-8 index.
3085
3060
  *
3086
3061
  * @example
3087
3062
  * ```ts
@@ -3089,13 +3064,41 @@ export class LoroText {
3089
3064
  *
3090
3065
  * const doc = new LoroDoc();
3091
3066
  * const text = doc.getText("text");
3092
- * text.insert(0, "Hello");
3093
- * text.slice(0, 2); // "He"
3067
+ * text.insertUtf8(0, "Hello");
3094
3068
  * ```
3095
3069
  */
3096
- slice(start_index: number, end_index: number): string;
3070
+ insertUtf8(index: number, content: string): void;
3097
3071
  /**
3098
- * Get the character at the given position (utf-16 index).
3072
+ * Whether the container is attached to a LoroDoc.
3073
+ *
3074
+ * If it's detached, the operations on the container will not be persisted.
3075
+ */
3076
+ isAttached(): boolean;
3077
+ /**
3078
+ * Get the attached container associated with this.
3079
+ *
3080
+ * Returns an attached `Container` that is equal to this or created by this; otherwise, it returns `undefined`.
3081
+ */
3082
+ getAttached(): LoroText | undefined;
3083
+ /**
3084
+ * Get the shallow value of the text. This equals to `text.toString()`.
3085
+ */
3086
+ getShallowValue(): string;
3087
+ /**
3088
+ * Create a new detached LoroText (not attached to any LoroDoc).
3089
+ *
3090
+ * The edits on a detached container will not be persisted.
3091
+ * To attach the container to the document, please insert it into an attached container.
3092
+ */
3093
+ constructor();
3094
+ /**
3095
+ * Iterate each text span(internal storage unit)
3096
+ *
3097
+ * The callback function will be called for each span in the text.
3098
+ * If the callback returns `false`, the iteration will stop.
3099
+ *
3100
+ * Limitation: you cannot access or alter the doc state when iterating (this is for performance consideration).
3101
+ * If you need to access or alter the doc state, please use `toString` instead.
3099
3102
  *
3100
3103
  * @example
3101
3104
  * ```ts
@@ -3104,26 +3107,39 @@ export class LoroText {
3104
3107
  * const doc = new LoroDoc();
3105
3108
  * const text = doc.getText("text");
3106
3109
  * text.insert(0, "Hello");
3107
- * text.charAt(0); // "H"
3110
+ * text.iter((str) => (console.log(str), true));
3108
3111
  * ```
3109
3112
  */
3110
- charAt(pos: number): string;
3113
+ iter(callback: (string) => boolean): void;
3111
3114
  /**
3112
- * Delete and return the string at the given range and insert a string at the same position (utf-16 index).
3115
+ * "Text"
3116
+ */
3117
+ kind(): 'Text';
3118
+ /**
3119
+ * Mark a range of text with a key and a value (utf-16 index).
3120
+ *
3121
+ * > You should call `configTextStyle` before using `mark` and `unmark`.
3122
+ *
3123
+ * You can use it to create a highlight, make a range of text bold, or add a link to a range of text.
3113
3124
  *
3114
3125
  * @example
3115
3126
  * ```ts
3116
3127
  * import { LoroDoc } from "loro-crdt";
3117
3128
  *
3118
3129
  * const doc = new LoroDoc();
3130
+ * doc.configTextStyle({bold: {expand: "after"}});
3119
3131
  * const text = doc.getText("text");
3120
- * text.insert(0, "Hello");
3121
- * text.splice(2, 3, "llo"); // "llo"
3132
+ * text.insert(0, "Hello World!");
3133
+ * text.mark({ start: 0, end: 5 }, "bold", true);
3122
3134
  * ```
3123
3135
  */
3124
- splice(pos: number, len: number, s: string): string;
3136
+ mark(range: { start: number, end: number }, key: string, value: any): void;
3125
3137
  /**
3126
- * Insert some string at utf-8 index.
3138
+ * Push a string to the end of the text.
3139
+ */
3140
+ push(s: string): void;
3141
+ /**
3142
+ * Get a string slice (utf-16 index).
3127
3143
  *
3128
3144
  * @example
3129
3145
  * ```ts
@@ -3131,10 +3147,11 @@ export class LoroText {
3131
3147
  *
3132
3148
  * const doc = new LoroDoc();
3133
3149
  * const text = doc.getText("text");
3134
- * text.insertUtf8(0, "Hello");
3150
+ * text.insert(0, "Hello");
3151
+ * text.slice(0, 2); // "He"
3135
3152
  * ```
3136
3153
  */
3137
- insertUtf8(index: number, content: string): void;
3154
+ slice(start_index: number, end_index: number): string;
3138
3155
  /**
3139
3156
  * Delete elements from index to index + len (utf-16 index).
3140
3157
  *
@@ -3152,7 +3169,7 @@ export class LoroText {
3152
3169
  */
3153
3170
  delete(index: number, len: number): void;
3154
3171
  /**
3155
- * Delete elements from index to utf-8 index + len
3172
+ * Insert the string at the given index (utf-16 index).
3156
3173
  *
3157
3174
  * @example
3158
3175
  * ```ts
@@ -3160,32 +3177,32 @@ export class LoroText {
3160
3177
  *
3161
3178
  * const doc = new LoroDoc();
3162
3179
  * const text = doc.getText("text");
3163
- * text.insertUtf8(0, "Hello");
3164
- * text.deleteUtf8(1, 3);
3165
- * const s = text.toString();
3166
- * console.log(s); // "Ho"
3180
+ * text.insert(0, "Hello");
3167
3181
  * ```
3168
3182
  */
3169
- deleteUtf8(index: number, len: number): void;
3183
+ insert(index: number, content: string): void;
3170
3184
  /**
3171
- * Mark a range of text with a key and a value (utf-16 index).
3172
- *
3173
- * > You should call `configTextStyle` before using `mark` and `unmark`.
3185
+ * Get the parent container.
3174
3186
  *
3175
- * You can use it to create a highlight, make a range of text bold, or add a link to a range of text.
3187
+ * - The parent of the root is `undefined`.
3188
+ * - The object returned is a new js object each time because it need to cross
3189
+ * the WASM boundary.
3190
+ */
3191
+ parent(): Container | undefined;
3192
+ /**
3193
+ * Delete and return the string at the given range and insert a string at the same position (utf-16 index).
3176
3194
  *
3177
3195
  * @example
3178
3196
  * ```ts
3179
3197
  * import { LoroDoc } from "loro-crdt";
3180
3198
  *
3181
3199
  * const doc = new LoroDoc();
3182
- * doc.configTextStyle({bold: {expand: "after"}});
3183
3200
  * const text = doc.getText("text");
3184
- * text.insert(0, "Hello World!");
3185
- * text.mark({ start: 0, end: 5 }, "bold", true);
3201
+ * text.insert(0, "Hello");
3202
+ * text.splice(2, 3, "llo"); // "llo"
3186
3203
  * ```
3187
3204
  */
3188
- mark(range: { start: number, end: number }, key: string, value: any): void;
3205
+ splice(pos: number, len: number, s: string): string;
3189
3206
  /**
3190
3207
  * Unmark a range of text with a key and a value (utf-16 index).
3191
3208
  *
@@ -3207,13 +3224,7 @@ export class LoroText {
3207
3224
  */
3208
3225
  unmark(range: { start: number, end: number }, key: string): void;
3209
3226
  /**
3210
- * Convert the text to a string
3211
- */
3212
- toString(): string;
3213
- /**
3214
- * Get the text in [Delta](https://quilljs.com/docs/delta/) format.
3215
- *
3216
- * The returned value will include the rich text information.
3227
+ * Get the character at the given position (utf-16 index).
3217
3228
  *
3218
3229
  * @example
3219
3230
  * ```ts
@@ -3221,80 +3232,41 @@ export class LoroText {
3221
3232
  *
3222
3233
  * const doc = new LoroDoc();
3223
3234
  * const text = doc.getText("text");
3224
- * doc.configTextStyle({bold: {expand: "after"}});
3225
- * text.insert(0, "Hello World!");
3226
- * text.mark({ start: 0, end: 5 }, "bold", true);
3227
- * console.log(text.toDelta()); // [ { insert: 'Hello', attributes: { bold: true } } ]
3235
+ * text.insert(0, "Hello");
3236
+ * text.charAt(0); // "H"
3228
3237
  * ```
3229
3238
  */
3230
- toDelta(): Delta<string>[];
3239
+ charAt(pos: number): string;
3231
3240
  /**
3232
- * Change the state of this text by delta.
3233
- *
3234
- * If a delta item is `insert`, it should include all the attributes of the inserted text.
3235
- * Loro's rich text CRDT may make the inserted text inherit some styles when you use
3236
- * `insert` method directly. However, when you use `applyDelta` if some attributes are
3237
- * inherited from CRDT but not included in the delta, they will be removed.
3241
+ * Get the JSON representation of the text.
3242
+ */
3243
+ toJSON(): any;
3244
+ /**
3245
+ * Get the text in [Delta](https://quilljs.com/docs/delta/) format.
3238
3246
  *
3239
- * Another special property of `applyDelta` is if you format an attribute for ranges out of
3240
- * the text length, Loro will insert new lines to fill the gap first. It's useful when you
3241
- * build the binding between Loro and rich text editors like Quill, which might assume there
3242
- * is always a newline at the end of the text implicitly.
3247
+ * The returned value will include the rich text information.
3243
3248
  *
3244
3249
  * @example
3245
3250
  * ```ts
3251
+ * import { LoroDoc } from "loro-crdt";
3252
+ *
3246
3253
  * const doc = new LoroDoc();
3247
3254
  * const text = doc.getText("text");
3248
3255
  * doc.configTextStyle({bold: {expand: "after"}});
3249
3256
  * text.insert(0, "Hello World!");
3250
3257
  * text.mark({ start: 0, end: 5 }, "bold", true);
3251
- * const delta = text.toDelta();
3252
- * const text2 = doc.getText("text2");
3253
- * text2.applyDelta(delta);
3254
- * expect(text2.toDelta()).toStrictEqual(delta);
3258
+ * console.log(text.toDelta()); // [ { insert: 'Hello', attributes: { bold: true } } ]
3255
3259
  * ```
3256
3260
  */
3257
- applyDelta(delta: Delta<string>[]): void;
3258
- /**
3259
- * Get the parent container.
3260
- *
3261
- * - The parent of the root is `undefined`.
3262
- * - The object returned is a new js object each time because it need to cross
3263
- * the WASM boundary.
3264
- */
3265
- parent(): Container | undefined;
3266
- /**
3267
- * Whether the container is attached to a LoroDoc.
3268
- *
3269
- * If it's detached, the operations on the container will not be persisted.
3270
- */
3271
- isAttached(): boolean;
3272
- /**
3273
- * Get the attached container associated with this.
3274
- *
3275
- * Returns an attached `Container` that is equal to this or created by this; otherwise, it returns `undefined`.
3276
- */
3277
- getAttached(): LoroText | undefined;
3278
- /**
3279
- * Push a string to the end of the text.
3280
- */
3281
- push(s: string): void;
3282
- /**
3283
- * Get the editor of the text at the given position.
3284
- */
3285
- getEditorOf(pos: number): PeerID | undefined;
3261
+ toDelta(): Delta<string>[];
3286
3262
  /**
3287
3263
  * Check if the container is deleted
3288
3264
  */
3289
3265
  isDeleted(): boolean;
3290
3266
  /**
3291
- * Get the shallow value of the text. This equals to `text.toString()`.
3292
- */
3293
- getShallowValue(): string;
3294
- /**
3295
- * Get the JSON representation of the text.
3267
+ * Convert the text to a string
3296
3268
  */
3297
- toJSON(): any;
3269
+ toString(): string;
3298
3270
  /**
3299
3271
  * Get the container id of the text.
3300
3272
  */
@@ -3312,61 +3284,90 @@ export class LoroText {
3312
3284
  export class LoroTree {
3313
3285
  free(): void;
3314
3286
  /**
3315
- * Create a new detached LoroTree (not attached to any LoroDoc).
3287
+ * Whether the container is attached to a document.
3316
3288
  *
3317
- * The edits on a detached container will not be persisted.
3318
- * To attach the container to the document, please insert it into an attached container.
3289
+ * If it's detached, the operations on the container will not be persisted.
3319
3290
  */
3320
- constructor();
3291
+ isAttached(): boolean;
3321
3292
  /**
3322
- * "Tree"
3293
+ * Get the attached container associated with this.
3294
+ *
3295
+ * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
3323
3296
  */
3324
- kind(): 'Tree';
3297
+ getAttached(): LoroTree | undefined;
3325
3298
  /**
3326
- * Move the target tree node to be a child of the parent.
3327
- * It's not allowed that the target is an ancestor of the parent
3328
- * or the target and the parent are the same node.
3299
+ * Return `None` if the node is not exist, otherwise return `Some(true)` if the node is deleted.
3300
+ */
3301
+ isNodeDeleted(target: TreeID): boolean;
3302
+ /**
3303
+ * Get the shallow value of the tree.
3304
+ *
3305
+ * Unlike `toJSON()` which recursively resolves nested containers to their values,
3306
+ * `getShallowValue()` returns container IDs as strings for any nested containers.
3329
3307
  *
3330
3308
  * @example
3331
3309
  * ```ts
3332
- * import { LoroDoc } from "loro-crdt";
3333
- *
3334
3310
  * const doc = new LoroDoc();
3311
+ * doc.setPeerId("1");
3335
3312
  * const tree = doc.getTree("tree");
3336
3313
  * const root = tree.createNode();
3337
- * const node = root.createNode();
3338
- * const node2 = node.createNode();
3339
- * tree.move(node2.id, root.id);
3340
- * // Error will be thrown if move operation creates a cycle
3341
- * // tree.move(root.id, node.id);
3314
+ * root.data.set("name", "root");
3315
+ * const text = root.data.setContainer("content", new LoroText());
3316
+ * text.insert(0, "Hello");
3317
+ *
3318
+ * console.log(tree.getShallowValue());
3319
+ * // [{
3320
+ * // id: "0@1",
3321
+ * // parent: null,
3322
+ * // index: 0,
3323
+ * // fractional_index: "80",
3324
+ * // meta: "cid:0@1:Map",
3325
+ * // children: []
3326
+ * // }]
3327
+ *
3328
+ * console.log(tree.toJSON());
3329
+ * // [{
3330
+ * // id: "0@1",
3331
+ * // parent: null,
3332
+ * // index: 0,
3333
+ * // fractional_index: "80",
3334
+ * // meta: {
3335
+ * // name: "root",
3336
+ * // content: "Hello"
3337
+ * // },
3338
+ * // children: []
3339
+ * // }]
3342
3340
  * ```
3343
3341
  */
3344
- move(target: TreeID, parent: TreeID | undefined, index?: number | null): void;
3342
+ getShallowValue(): TreeNodeShallowValue[];
3345
3343
  /**
3346
- * Delete a tree node from the forest.
3344
+ * Set whether to generate a fractional index for moving and creating.
3347
3345
  *
3348
- * @example
3349
- * ```ts
3350
- * import { LoroDoc } from "loro-crdt";
3346
+ * A fractional index can be used to determine the position of tree nodes among their siblings.
3351
3347
  *
3352
- * const doc = new LoroDoc();
3353
- * const tree = doc.getTree("tree");
3354
- * const root = tree.createNode();
3355
- * const node = root.createNode();
3356
- * tree.delete(node.id);
3357
- * ```
3348
+ * The jitter is used to avoid conflicts when multiple users are creating a node at the same position.
3349
+ * A value of 0 is the default, which means no jitter; any value larger than 0 will enable jitter.
3350
+ *
3351
+ * Generally speaking, higher jitter value will increase the size of the operation
3352
+ * [Read more about it](https://www.loro.dev/blog/movable-tree#implementation-and-encoding-size)
3358
3353
  */
3359
- delete(target: TreeID): void;
3354
+ enableFractionalIndex(jitter: number): void;
3360
3355
  /**
3361
- * Return `true` if the tree contains the TreeID, include deleted node.
3356
+ * Disable the fractional index generation when you don't need the Tree's siblings to be sorted.
3357
+ * The fractional index will always be set to the same default value 0.
3358
+ *
3359
+ * After calling this, you cannot use `tree.moveTo()`, `tree.moveBefore()`, `tree.moveAfter()`,
3360
+ * and `tree.createAt()`.
3362
3361
  */
3363
- has(target: TreeID): boolean;
3362
+ disableFractionalIndex(): void;
3364
3363
  /**
3365
- * Return `None` if the node is not exist, otherwise return `Some(true)` if the node is deleted.
3364
+ * Whether the tree enables the fractional index generation.
3366
3365
  */
3367
- isNodeDeleted(target: TreeID): boolean;
3366
+ isFractionalIndexEnabled(): boolean;
3368
3367
  /**
3369
- * Get the hierarchy array with metadata of the forest.
3368
+ * Move the target tree node to be a child of the parent.
3369
+ * It's not allowed that the target is an ancestor of the parent
3370
+ * or the target and the parent are the same node.
3370
3371
  *
3371
3372
  * @example
3372
3373
  * ```ts
@@ -3375,12 +3376,25 @@ export class LoroTree {
3375
3376
  * const doc = new LoroDoc();
3376
3377
  * const tree = doc.getTree("tree");
3377
3378
  * const root = tree.createNode();
3378
- * root.data.set("color", "red");
3379
- * // [ { id: '0@F2462C4159C4C8D1', parent: null, meta: { color: 'red' }, children: [] } ]
3380
- * console.log(tree.toJSON());
3379
+ * const node = root.createNode();
3380
+ * const node2 = node.createNode();
3381
+ * tree.move(node2.id, root.id);
3382
+ * // Error will be thrown if move operation creates a cycle
3383
+ * // tree.move(root.id, node.id);
3381
3384
  * ```
3382
3385
  */
3383
- toJSON(): any;
3386
+ move(target: TreeID, parent: TreeID | undefined, index?: number | null): void;
3387
+ /**
3388
+ * Create a new detached LoroTree (not attached to any LoroDoc).
3389
+ *
3390
+ * The edits on a detached container will not be persisted.
3391
+ * To attach the container to the document, please insert it into an attached container.
3392
+ */
3393
+ constructor();
3394
+ /**
3395
+ * "Tree"
3396
+ */
3397
+ kind(): 'Tree';
3384
3398
  /**
3385
3399
  * Get all tree nodes of the forest, including deleted nodes.
3386
3400
  *
@@ -3393,103 +3407,61 @@ export class LoroTree {
3393
3407
  * const root = tree.createNode();
3394
3408
  * const node = root.createNode();
3395
3409
  * const node2 = node.createNode();
3396
- * console.log(tree.nodes());
3397
- * ```
3398
- */
3399
- nodes(): LoroTreeNode[];
3400
- /**
3401
- * Get the root nodes of the forest.
3402
- */
3403
- roots(): LoroTreeNode[];
3404
- /**
3405
- * Get the parent container of the tree container.
3406
- *
3407
- * - The parent container of the root tree is `undefined`.
3408
- * - The object returned is a new js object each time because it need to cross
3409
- * the WASM boundary.
3410
- */
3411
- parent(): Container | undefined;
3412
- /**
3413
- * Whether the container is attached to a document.
3414
- *
3415
- * If it's detached, the operations on the container will not be persisted.
3410
+ * console.log(tree.nodes());
3411
+ * ```
3416
3412
  */
3417
- isAttached(): boolean;
3413
+ nodes(): LoroTreeNode[];
3418
3414
  /**
3419
- * Get the attached container associated with this.
3420
- *
3421
- * Returns an attached `Container` that equals to this or created by this, otherwise `undefined`.
3415
+ * Get the root nodes of the forest.
3422
3416
  */
3423
- getAttached(): LoroTree | undefined;
3417
+ roots(): LoroTreeNode[];
3424
3418
  /**
3425
- * Set whether to generate a fractional index for moving and creating.
3426
- *
3427
- * A fractional index can be used to determine the position of tree nodes among their siblings.
3419
+ * Delete a tree node from the forest.
3428
3420
  *
3429
- * The jitter is used to avoid conflicts when multiple users are creating a node at the same position.
3430
- * A value of 0 is the default, which means no jitter; any value larger than 0 will enable jitter.
3421
+ * @example
3422
+ * ```ts
3423
+ * import { LoroDoc } from "loro-crdt";
3431
3424
  *
3432
- * Generally speaking, higher jitter value will increase the size of the operation
3433
- * [Read more about it](https://www.loro.dev/blog/movable-tree#implementation-and-encoding-size)
3425
+ * const doc = new LoroDoc();
3426
+ * const tree = doc.getTree("tree");
3427
+ * const root = tree.createNode();
3428
+ * const node = root.createNode();
3429
+ * tree.delete(node.id);
3430
+ * ```
3434
3431
  */
3435
- enableFractionalIndex(jitter: number): void;
3432
+ delete(target: TreeID): void;
3436
3433
  /**
3437
- * Disable the fractional index generation when you don't need the Tree's siblings to be sorted.
3438
- * The fractional index will always be set to the same default value 0.
3434
+ * Get the parent container of the tree container.
3439
3435
  *
3440
- * After calling this, you cannot use `tree.moveTo()`, `tree.moveBefore()`, `tree.moveAfter()`,
3441
- * and `tree.createAt()`.
3442
- */
3443
- disableFractionalIndex(): void;
3444
- /**
3445
- * Whether the tree enables the fractional index generation.
3446
- */
3447
- isFractionalIndexEnabled(): boolean;
3448
- /**
3449
- * Check if the container is deleted
3436
+ * - The parent container of the root tree is `undefined`.
3437
+ * - The object returned is a new js object each time because it need to cross
3438
+ * the WASM boundary.
3450
3439
  */
3451
- isDeleted(): boolean;
3440
+ parent(): Container | undefined;
3452
3441
  /**
3453
- * Get the shallow value of the tree.
3454
- *
3455
- * Unlike `toJSON()` which recursively resolves nested containers to their values,
3456
- * `getShallowValue()` returns container IDs as strings for any nested containers.
3442
+ * Get the hierarchy array with metadata of the forest.
3457
3443
  *
3458
3444
  * @example
3459
3445
  * ```ts
3446
+ * import { LoroDoc } from "loro-crdt";
3447
+ *
3460
3448
  * const doc = new LoroDoc();
3461
- * doc.setPeerId("1");
3462
3449
  * const tree = doc.getTree("tree");
3463
3450
  * const root = tree.createNode();
3464
- * root.data.set("name", "root");
3465
- * const text = root.data.setContainer("content", new LoroText());
3466
- * text.insert(0, "Hello");
3467
- *
3468
- * console.log(tree.getShallowValue());
3469
- * // [{
3470
- * // id: "0@1",
3471
- * // parent: null,
3472
- * // index: 0,
3473
- * // fractional_index: "80",
3474
- * // meta: "cid:0@1:Map",
3475
- * // children: []
3476
- * // }]
3477
- *
3451
+ * root.data.set("color", "red");
3452
+ * // [ { id: '0@F2462C4159C4C8D1', parent: null, meta: { color: 'red' }, children: [] } ]
3478
3453
  * console.log(tree.toJSON());
3479
- * // [{
3480
- * // id: "0@1",
3481
- * // parent: null,
3482
- * // index: 0,
3483
- * // fractional_index: "80",
3484
- * // meta: {
3485
- * // name: "root",
3486
- * // content: "Hello"
3487
- * // },
3488
- * // children: []
3489
- * // }]
3490
3454
  * ```
3491
3455
  */
3492
- getShallowValue(): TreeNodeShallowValue[];
3456
+ toJSON(): any;
3457
+ /**
3458
+ * Return `true` if the tree contains the TreeID, include deleted node.
3459
+ */
3460
+ has(target: TreeID): boolean;
3461
+ /**
3462
+ * Check if the container is deleted
3463
+ */
3464
+ isDeleted(): boolean;
3493
3465
  /**
3494
3466
  * Get the id of the container.
3495
3467
  */
@@ -3501,26 +3473,14 @@ export class LoroTree {
3501
3473
  export class LoroTreeNode {
3502
3474
  private constructor();
3503
3475
  free(): void;
3504
- __getClassname(): string;
3505
3476
  /**
3506
- * Move the tree node to be after the target node.
3507
- *
3508
- * @example
3509
- * ```ts
3510
- * import { LoroDoc } from "loro-crdt";
3511
- *
3512
- * const doc = new LoroDoc();
3513
- * const tree = doc.getTree("tree");
3514
- * const root = tree.createNode();
3515
- * const node = root.createNode();
3516
- * const node2 = root.createNode();
3517
- * node2.moveAfter(node);
3518
- * // root
3519
- * // / \
3520
- * // node node2
3521
- * ```
3477
+ * Get the creation id of this node.
3522
3478
  */
3523
- moveAfter(target: LoroTreeNode): void;
3479
+ creationId(): { peer: PeerID, counter: number };
3480
+ /**
3481
+ * Check if the node is deleted.
3482
+ */
3483
+ isDeleted(): boolean;
3524
3484
  /**
3525
3485
  * Move the tree node to be before the target node.
3526
3486
  *
@@ -3541,31 +3501,43 @@ export class LoroTreeNode {
3541
3501
  */
3542
3502
  moveBefore(target: LoroTreeNode): void;
3543
3503
  /**
3544
- * Get the index of the node in the parent's children.
3504
+ * Get the last mover of this node.
3545
3505
  */
3546
- index(): number | undefined;
3506
+ getLastMoveId(): { peer: PeerID, counter: number } | undefined;
3547
3507
  /**
3548
3508
  * Get the `Fractional Index` of the node.
3549
3509
  *
3550
3510
  * Note: the tree container must be attached to the document.
3551
3511
  */
3552
3512
  fractionalIndex(): string | undefined;
3513
+ __getClassname(): string;
3553
3514
  /**
3554
- * Check if the node is deleted.
3555
- */
3556
- isDeleted(): boolean;
3557
- /**
3558
- * Get the last mover of this node.
3559
- */
3560
- getLastMoveId(): { peer: PeerID, counter: number } | undefined;
3561
- /**
3562
- * Get the creation id of this node.
3515
+ * Get the index of the node in the parent's children.
3563
3516
  */
3564
- creationId(): { peer: PeerID, counter: number };
3517
+ index(): number | undefined;
3565
3518
  /**
3566
3519
  * Get the creator of this node.
3567
3520
  */
3568
3521
  creator(): PeerID;
3522
+ /**
3523
+ * Move the tree node to be after the target node.
3524
+ *
3525
+ * @example
3526
+ * ```ts
3527
+ * import { LoroDoc } from "loro-crdt";
3528
+ *
3529
+ * const doc = new LoroDoc();
3530
+ * const tree = doc.getTree("tree");
3531
+ * const root = tree.createNode();
3532
+ * const node = root.createNode();
3533
+ * const node2 = root.createNode();
3534
+ * node2.moveAfter(node);
3535
+ * // root
3536
+ * // / \
3537
+ * // node node2
3538
+ * ```
3539
+ */
3540
+ moveAfter(target: LoroTreeNode): void;
3569
3541
  /**
3570
3542
  * The TreeID of the node.
3571
3543
  */
@@ -3587,6 +3559,33 @@ export class LoroTreeNode {
3587
3559
  */
3588
3560
  export class UndoManager {
3589
3561
  free(): void;
3562
+ /**
3563
+ * Get the value associated with the top redo stack item, if any.
3564
+ * Returns `undefined` if there is no redo item.
3565
+ */
3566
+ topRedoValue(): Value | undefined;
3567
+ /**
3568
+ * Get the value associated with the top undo stack item, if any.
3569
+ * Returns `undefined` if there is no undo item.
3570
+ */
3571
+ topUndoValue(): Value | undefined;
3572
+ /**
3573
+ * The number of max undo steps.
3574
+ * If the number of undo steps exceeds this number, the oldest undo step will be removed.
3575
+ */
3576
+ setMaxUndoSteps(steps: number): void;
3577
+ /**
3578
+ * Set the merge interval (in ms).
3579
+ *
3580
+ * If the interval is set to 0, the undo steps will not be merged.
3581
+ * Otherwise, the undo steps will be merged if the interval between the two steps is less than the given interval.
3582
+ */
3583
+ setMergeInterval(interval: number): void;
3584
+ /**
3585
+ * If a local event's origin matches the given prefix, it will not be recorded in the
3586
+ * undo stack.
3587
+ */
3588
+ addExcludeOriginPrefix(prefix: string): void;
3590
3589
  /**
3591
3590
  * `UndoManager` is responsible for handling undo and redo operations.
3592
3591
  *
@@ -3611,53 +3610,26 @@ export class UndoManager {
3611
3610
  */
3612
3611
  constructor(doc: LoroDoc, config: UndoConfig);
3613
3612
  /**
3614
- * Undo the last operation.
3613
+ * Get the peer id of the undo manager.
3615
3614
  */
3616
- undo(): boolean;
3615
+ peer(): PeerID;
3617
3616
  /**
3618
3617
  * Redo the last undone operation.
3619
3618
  */
3620
3619
  redo(): boolean;
3621
3620
  /**
3622
- * Get the peer id of the undo manager.
3623
- */
3624
- peer(): PeerID;
3625
- /**
3626
- * Can undo the last operation.
3621
+ * Undo the last operation.
3627
3622
  */
3628
- canUndo(): boolean;
3623
+ undo(): boolean;
3624
+ clear(): void;
3629
3625
  /**
3630
3626
  * Can redo the last operation.
3631
3627
  */
3632
3628
  canRedo(): boolean;
3633
3629
  /**
3634
- * Get the value associated with the top undo stack item, if any.
3635
- * Returns `undefined` if there is no undo item.
3636
- */
3637
- topUndoValue(): Value | undefined;
3638
- /**
3639
- * Get the value associated with the top redo stack item, if any.
3640
- * Returns `undefined` if there is no redo item.
3641
- */
3642
- topRedoValue(): Value | undefined;
3643
- /**
3644
- * The number of max undo steps.
3645
- * If the number of undo steps exceeds this number, the oldest undo step will be removed.
3646
- */
3647
- setMaxUndoSteps(steps: number): void;
3648
- /**
3649
- * Set the merge interval (in ms).
3650
- *
3651
- * If the interval is set to 0, the undo steps will not be merged.
3652
- * Otherwise, the undo steps will be merged if the interval between the two steps is less than the given interval.
3653
- */
3654
- setMergeInterval(interval: number): void;
3655
- /**
3656
- * If a local event's origin matches the given prefix, it will not be recorded in the
3657
- * undo stack.
3630
+ * Can undo the last operation.
3658
3631
  */
3659
- addExcludeOriginPrefix(prefix: string): void;
3660
- clear(): void;
3632
+ canUndo(): boolean;
3661
3633
  }
3662
3634
  /**
3663
3635
  * [VersionVector](https://en.wikipedia.org/wiki/Version_vector)
@@ -3669,43 +3641,43 @@ export class UndoManager {
3669
3641
  export class VersionVector {
3670
3642
  free(): void;
3671
3643
  /**
3672
- * Create a new version vector.
3644
+ * Get the counter of a peer.
3673
3645
  */
3674
- constructor(value: Map<PeerID, number> | Uint8Array | VersionVector | undefined | null);
3646
+ get(peer_id: number | bigint | `${number}`): number | undefined;
3675
3647
  /**
3676
- * Create a new version vector from a Map.
3648
+ * Create a new version vector.
3677
3649
  */
3678
- static parseJSON(version: Map<PeerID, number>): VersionVector;
3650
+ constructor(value: Map<PeerID, number> | Uint8Array | VersionVector | undefined | null);
3679
3651
  /**
3680
- * Convert the version vector to a Map
3652
+ * Decode the version vector from a Uint8Array.
3681
3653
  */
3682
- toJSON(): Map<PeerID, number>;
3654
+ static decode(bytes: Uint8Array): VersionVector;
3683
3655
  /**
3684
3656
  * Encode the version vector into a Uint8Array.
3685
3657
  */
3686
3658
  encode(): Uint8Array;
3659
+ length(): number;
3660
+ remove(peer: PeerID): void;
3687
3661
  /**
3688
- * Decode the version vector from a Uint8Array.
3689
- */
3690
- static decode(bytes: Uint8Array): VersionVector;
3691
- /**
3692
- * Get the counter of a peer.
3662
+ * set the exclusive ending point. target id will NOT be included by self
3693
3663
  */
3694
- get(peer_id: number | bigint | `${number}`): number | undefined;
3664
+ setEnd(id: { peer: PeerID, counter: number }): void;
3695
3665
  /**
3696
3666
  * Compare the version vector with another version vector.
3697
3667
  *
3698
3668
  * If they are concurrent, return undefined.
3699
3669
  */
3700
3670
  compare(other: VersionVector): number | undefined;
3701
- /**
3702
- * set the exclusive ending point. target id will NOT be included by self
3703
- */
3704
- setEnd(id: { peer: PeerID, counter: number }): void;
3705
3671
  /**
3706
3672
  * set the inclusive ending point. target id will be included
3707
3673
  */
3708
3674
  setLast(id: { peer: PeerID, counter: number }): void;
3709
- remove(peer: PeerID): void;
3710
- length(): number;
3675
+ /**
3676
+ * Convert the version vector to a Map
3677
+ */
3678
+ toJSON(): Map<PeerID, number>;
3679
+ /**
3680
+ * Create a new version vector from a Map.
3681
+ */
3682
+ static parseJSON(version: Map<PeerID, number>): VersionVector;
3711
3683
  }