loro-crdt 1.2.6 → 1.3.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.
@@ -172,7 +172,7 @@ interface LoroDoc {
172
172
  * const doc = new LoroDoc();
173
173
  * const text = doc.getText("text");
174
174
  * text.insert(0, "Hello");
175
- * text.mark(0, 2, {bold: true});
175
+ * text.mark({ start: 0, end: 2 }, "bold", true);
176
176
  *
177
177
  * // Use delta to represent text
178
178
  * const json = doc.toJsonWithReplacer((key, value) => {
@@ -264,6 +264,27 @@ export type Value =
264
264
  | Value[]
265
265
  | undefined;
266
266
 
267
+ export type IdSpan = {
268
+ peer: PeerID,
269
+ counter: number,
270
+ length: number,
271
+ }
272
+
273
+ export type VersionVectorDiff = {
274
+ /**
275
+ * The spans that the `from` side needs to retreat to reach the `to` side
276
+ *
277
+ * These spans are included in the `from`, but not in the `to`
278
+ */
279
+ retreat: IdSpan[],
280
+ /**
281
+ * The spans that the `from` side needs to forward to reach the `to` side
282
+ *
283
+ * These spans are included in the `to`, but not in the `from`
284
+ */
285
+ forward: IdSpan[],
286
+ }
287
+
267
288
  export type UndoConfig = {
268
289
  mergeInterval?: number,
269
290
  maxUndoSteps?: number,
@@ -397,11 +418,6 @@ export type TreeNodeJSON<T> = Omit<TreeNodeValue, 'meta' | 'children'> & {
397
418
  children: TreeNodeJSON<T>[],
398
419
  }
399
420
 
400
- interface LoroTree{
401
- toArray(): TreeNodeValue[];
402
- getNodes(options?: { withDeleted?: boolean } ): LoroTreeNode[];
403
- }
404
-
405
421
  interface LoroMovableList {
406
422
  /**
407
423
  * Get the cursor position at the given pos.
@@ -632,6 +648,8 @@ export interface LoroEventBatch {
632
648
  */
633
649
  currentTarget?: ContainerID;
634
650
  events: LoroEvent[];
651
+ from: Frontiers;
652
+ to: Frontiers;
635
653
  }
636
654
 
637
655
  /**
@@ -798,9 +816,19 @@ interface LoroDoc<T extends Record<string, Container> = Record<string, Container
798
816
  *
799
817
  * @param start - The start version vector.
800
818
  * @param end - The end version vector.
819
+ * @param withPeerCompression - Whether to compress the peer IDs in the updates. Defaults to true. If you want to process the operations in application code, set this to false.
801
820
  * @returns The updates in the given range.
802
821
  */
803
- exportJsonUpdates(start?: VersionVector, end?: VersionVector): JsonSchema;
822
+ exportJsonUpdates(start?: VersionVector, end?: VersionVector, withPeerCompression?: boolean): JsonSchema;
823
+ /**
824
+ * Export the readable [`Change`]s in the given [`IdSpan`].
825
+ *
826
+ * The peers are not compressed in the returned changes.
827
+ *
828
+ * @param idSpan - The id span to export.
829
+ * @returns The changes in the given id span.
830
+ */
831
+ exportJsonInIdSpan(idSpan: IdSpan): JsonChange[];
804
832
  }
805
833
  interface LoroList<T = unknown> {
806
834
  new(): LoroList<T>;
@@ -1129,6 +1157,8 @@ interface LoroTree<T extends Record<string, unknown> = Record<string, unknown>>
1129
1157
  */
1130
1158
  getNodeByID(target: TreeID): LoroTreeNode<T>;
1131
1159
  subscribe(listener: Listener): Subscription;
1160
+ toArray(): TreeNodeValue[];
1161
+ getNodes(options?: { withDeleted?: boolean } ): LoroTreeNode<T>[];
1132
1162
  }
1133
1163
  interface LoroTreeNode<T extends Record<string, unknown> = Record<string, unknown>> {
1134
1164
  /**
@@ -1156,7 +1186,36 @@ interface LoroTreeNode<T extends Record<string, unknown> = Record<string, unknow
1156
1186
  * ```
1157
1187
  */
1158
1188
  createNode(index?: number): LoroTreeNode<T>;
1189
+ /**
1190
+ * Move this tree node to be a child of the parent.
1191
+ * If the parent is undefined, this node will be a root node.
1192
+ *
1193
+ * If the index is not provided, the node will be appended to the end.
1194
+ *
1195
+ * It's not allowed that the target is an ancestor of the parent.
1196
+ *
1197
+ * @example
1198
+ * ```ts
1199
+ * const doc = new LoroDoc();
1200
+ * const tree = doc.getTree("tree");
1201
+ * const root = tree.createNode();
1202
+ * const node = root.createNode();
1203
+ * const node2 = node.createNode();
1204
+ * node2.move(undefined, 0);
1205
+ * // node2 root
1206
+ * // |
1207
+ * // node
1208
+ *
1209
+ * ```
1210
+ */
1159
1211
  move(parent?: LoroTreeNode<T>, index?: number): void;
1212
+ /**
1213
+ * Get the parent node of this node.
1214
+ *
1215
+ * - The parent of the root node is `undefined`.
1216
+ * - The object returned is a new js object each time because it need to cross
1217
+ * the WASM boundary.
1218
+ */
1160
1219
  parent(): LoroTreeNode<T> | undefined;
1161
1220
  /**
1162
1221
  * Get the children of this node.
@@ -1443,9 +1502,12 @@ export class LoroDoc {
1443
1502
  */
1444
1503
  setRecordTimestamp(auto_record: boolean): void;
1445
1504
  /**
1446
- * If two continuous local changes are within the interval, they will be merged into one change.
1505
+ * If two continuous local changes are within (<=) the interval(**in seconds**), they will be merged into one change.
1506
+ *
1507
+ * The default value is 1_000 seconds.
1447
1508
  *
1448
- * The default value is 1_000_000, the default unit is seconds.
1509
+ * By default, we record timestamps in seconds for each change. So if the merge interval is 1, and changes A and B
1510
+ * have timestamps of 3 and 4 respectively, then they will be merged into one change
1449
1511
  * @param {number} interval
1450
1512
  */
1451
1513
  setChangeMergeInterval(interval: number): void;
@@ -1627,6 +1689,54 @@ export class LoroDoc {
1627
1689
  */
1628
1690
  travelChangeAncestors(ids: ({ peer: PeerID, counter: number })[], f: (change: Change) => boolean): void;
1629
1691
  /**
1692
+ * Find the op id spans that between the `from` version and the `to` version.
1693
+ *
1694
+ * You can combine it with `exportJsonInIdSpan` to get the changes between two versions.
1695
+ *
1696
+ * You can use it to travel all the changes from `from` to `to`. `from` and `to` are frontiers,
1697
+ * and they can be concurrent to each other. You can use it to find all the changes related to an event:
1698
+ *
1699
+ * @example
1700
+ * ```ts
1701
+ * import { LoroDoc } from "loro-crdt";
1702
+ *
1703
+ * const docA = new LoroDoc();
1704
+ * docA.setPeerId("1");
1705
+ * const docB = new LoroDoc();
1706
+ *
1707
+ * docA.getText("text").update("Hello");
1708
+ * docA.commit();
1709
+ * const snapshot = docA.export({ mode: "snapshot" });
1710
+ * let done = false;
1711
+ * docB.subscribe(e => {
1712
+ * const spans = docB.findIdSpansBetween(e.from, e.to);
1713
+ * const changes = docB.exportJsonInIdSpan(spans.forward[0]);
1714
+ * console.log(changes);
1715
+ * // [{
1716
+ * // id: "0@1",
1717
+ * // timestamp: expect.any(Number),
1718
+ * // deps: [],
1719
+ * // lamport: 0,
1720
+ * // msg: undefined,
1721
+ * // ops: [{
1722
+ * // container: "cid:root-text:Text",
1723
+ * // counter: 0,
1724
+ * // content: {
1725
+ * // type: "insert",
1726
+ * // pos: 0,
1727
+ * // text: "Hello"
1728
+ * // }
1729
+ * // }]
1730
+ * // }]
1731
+ * });
1732
+ * docB.import(snapshot);
1733
+ * ```
1734
+ * @param {({ peer: PeerID, counter: number })[]} from
1735
+ * @param {({ peer: PeerID, counter: number })[]} to
1736
+ * @returns {VersionVectorDiff}
1737
+ */
1738
+ findIdSpansBetween(from: ({ peer: PeerID, counter: number })[], to: ({ peer: PeerID, counter: number })[]): VersionVectorDiff;
1739
+ /**
1630
1740
  * Checkout the `DocState` to a specific version.
1631
1741
  *
1632
1742
  * > The document becomes detached during a `checkout` operation.
@@ -1663,12 +1773,14 @@ export class LoroDoc {
1663
1773
  */
1664
1774
  setPeerId(peer_id: number | bigint | `${number}`): void;
1665
1775
  /**
1666
- * Commit the cumulative auto committed transaction.
1776
+ * Commit the cumulative auto-committed transaction.
1667
1777
  *
1668
1778
  * You can specify the `origin`, `timestamp`, and `message` of the commit.
1669
1779
  *
1670
1780
  * - The `origin` is used to mark the event
1671
1781
  * - The `message` works like a git commit message, which will be recorded and synced to peers
1782
+ * - The `timestamp` is the number of seconds that have elapsed since 00:00:00 UTC on January 1, 1970.
1783
+ * It defaults to `Date.now() / 1000` when timestamp recording is enabled
1672
1784
  *
1673
1785
  * The events will be emitted after a transaction is committed. A transaction is committed when:
1674
1786
  *
@@ -1988,7 +2100,7 @@ export class LoroDoc {
1988
2100
  *
1989
2101
  * @example
1990
2102
  * ```ts
1991
- * import { LoroDoc, LoroText } from "loro-crdt";
2103
+ * import { LoroDoc, LoroText, LoroMap } from "loro-crdt";
1992
2104
  *
1993
2105
  * const doc = new LoroDoc();
1994
2106
  * const list = doc.getList("list");
@@ -2169,6 +2281,40 @@ export class LoroDoc {
2169
2281
  */
2170
2282
  getChangedContainersIn(id: { peer: PeerID, counter: number }, len: number): (ContainerID)[];
2171
2283
  /**
2284
+ * Revert the document to the given frontiers.
2285
+ *
2286
+ * The doc will not become detached when using this method. Instead, it will generate a series
2287
+ * of operations to revert the document to the given version.
2288
+ *
2289
+ * @example
2290
+ * ```ts
2291
+ * const doc = new LoroDoc();
2292
+ * doc.setPeerId("1");
2293
+ * const text = doc.getText("text");
2294
+ * text.insert(0, "Hello");
2295
+ * doc.commit();
2296
+ * doc.revertTo([{ peer: "1", counter: 1 }]);
2297
+ * expect(doc.getText("text").toString()).toBe("He");
2298
+ * ```
2299
+ * @param {({ peer: PeerID, counter: number })[]} frontiers
2300
+ */
2301
+ revertTo(frontiers: ({ peer: PeerID, counter: number })[]): void;
2302
+ /**
2303
+ * Apply a diff batch to the document
2304
+ * @param {Record<ContainerID, Diff>} diff
2305
+ */
2306
+ applyDiff(diff: Record<ContainerID, Diff>): void;
2307
+ /**
2308
+ * Calculate the differences between two frontiers
2309
+ *
2310
+ * The entries in the returned object are sorted by causal order: the creation of a child container will be
2311
+ * presented before its use.
2312
+ * @param {({ peer: PeerID, counter: number })[]} from
2313
+ * @param {({ peer: PeerID, counter: number })[]} to
2314
+ * @returns {Record<ContainerID, Diff>}
2315
+ */
2316
+ diff(from: ({ peer: PeerID, counter: number })[], to: ({ peer: PeerID, counter: number })[]): Record<ContainerID, Diff>;
2317
+ /**
2172
2318
  * Peer ID of the current writer.
2173
2319
  */
2174
2320
  readonly peerId: bigint;
@@ -2462,7 +2608,7 @@ export class LoroMap {
2462
2608
  *
2463
2609
  * @example
2464
2610
  * ```ts
2465
- * import { LoroDoc } from "loro-crdt";
2611
+ * import { LoroDoc, LoroText } from "loro-crdt";
2466
2612
  *
2467
2613
  * const doc = new LoroDoc();
2468
2614
  * doc.setPeerId("1");
@@ -3194,31 +3340,6 @@ export class LoroTreeNode {
3194
3340
  */
3195
3341
  __getClassname(): string;
3196
3342
  /**
3197
- * Move this tree node to be a child of the parent.
3198
- * If the parent is undefined, this node will be a root node.
3199
- *
3200
- * If the index is not provided, the node will be appended to the end.
3201
- *
3202
- * It's not allowed that the target is an ancestor of the parent.
3203
- *
3204
- * @example
3205
- * ```ts
3206
- * const doc = new LoroDoc();
3207
- * const tree = doc.getTree("tree");
3208
- * const root = tree.createNode();
3209
- * const node = root.createNode();
3210
- * const node2 = node.createNode();
3211
- * node2.move(undefined, 0);
3212
- * // node2 root
3213
- * // |
3214
- * // node
3215
- *
3216
- * ```
3217
- * @param {LoroTreeNode | undefined} parent
3218
- * @param {number | undefined} [index]
3219
- */
3220
- move(parent: LoroTreeNode | undefined, index?: number): void;
3221
- /**
3222
3343
  * Move the tree node to be after the target node.
3223
3344
  *
3224
3345
  * @example
@@ -3271,15 +3392,6 @@ export class LoroTreeNode {
3271
3392
  */
3272
3393
  fractionalIndex(): string | undefined;
3273
3394
  /**
3274
- * Get the parent node of this node.
3275
- *
3276
- * - The parent of the root node is `undefined`.
3277
- * - The object returned is a new js object each time because it need to cross
3278
- * the WASM boundary.
3279
- * @returns {LoroTreeNode | undefined}
3280
- */
3281
- parent(): LoroTreeNode | undefined;
3282
- /**
3283
3395
  * Check if the node is deleted.
3284
3396
  * @returns {boolean}
3285
3397
  */
@@ -3373,6 +3485,7 @@ export class UndoManager {
3373
3485
  setMaxUndoSteps(steps: number): void;
3374
3486
  /**
3375
3487
  * Set the merge interval (in ms).
3488
+ *
3376
3489
  * If the interval is set to 0, the undo steps will not be merged.
3377
3490
  * Otherwise, the undo steps will be merged if the interval between the two steps is less than the given interval.
3378
3491
  * @param {number} interval