@pluv/crdt-loro 0.34.1 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/crdt-loro@0.34.1 build /home/runner/work/pluv/pluv/packages/crdt-loro
2
+ > @pluv/crdt-loro@0.35.1 build /home/runner/work/pluv/pluv/packages/crdt-loro
3
3
  > tsup src/index.ts --format esm,cjs --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,11 +8,11 @@
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- ESM dist/index.mjs 8.13 KB
12
- ESM ⚡️ Build success in 76ms
13
- CJS dist/index.js 9.20 KB
14
- CJS ⚡️ Build success in 77ms
11
+ CJS dist/index.js 9.91 KB
12
+ CJS ⚡️ Build success in 129ms
13
+ ESM dist/index.mjs 8.81 KB
14
+ ESM ⚡️ Build success in 131ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 1813ms
17
- DTS dist/index.d.mts 3.52 KB
18
- DTS dist/index.d.ts 3.52 KB
16
+ DTS ⚡️ Build success in 2274ms
17
+ DTS dist/index.d.mts 3.48 KB
18
+ DTS dist/index.d.ts 3.48 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @pluv/crdt-loro
2
2
 
3
+ ## 0.35.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 9822786: Added missing `Tree` and `MovableList` types.
8
+ - 3cd6571: Added support for undo/redo for `@pluv/crdt-loro`.
9
+ - @pluv/crdt@0.35.1
10
+ - @pluv/types@0.35.1
11
+
12
+ ## 0.35.0
13
+
14
+ ### Patch Changes
15
+
16
+ - @pluv/crdt@0.35.0
17
+ - @pluv/types@0.35.0
18
+
3
19
  ## 0.34.1
4
20
 
5
21
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CrdtType, AbstractCrdtDoc, DocApplyEncodedStateParams, InferCrdtJson, DocSubscribeCallbackParams, AbstractCrdtDocFactory } from '@pluv/crdt';
2
- import { LoroDoc, LoroList, LoroMap, LoroText } from 'loro-crdt';
2
+ import { LoroDoc, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree } from 'loro-crdt';
3
3
 
4
4
  type LoroType<TValue extends unknown, TJson extends unknown = any> = TValue & CrdtType<TValue, TJson> & {
5
5
  toJSON?: () => any;
@@ -9,18 +9,11 @@ type LoroType<TValue extends unknown, TJson extends unknown = any> = TValue & Cr
9
9
  declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDoc<TStorage> {
10
10
  value: LoroDoc;
11
11
  private _storage;
12
+ private _undoManager;
12
13
  constructor(value?: TStorage);
13
14
  applyEncodedState(params: DocApplyEncodedStateParams): this;
14
15
  batchApplyEncodedState(updates: readonly (DocApplyEncodedStateParams | string | null | undefined)[]): this;
15
- /**
16
- * TODO
17
- * @description This method is not yet supported for loro
18
- */
19
16
  canRedo(): boolean;
20
- /**
21
- * TODO
22
- * @description This method is not yet supported for loro
23
- */
24
17
  canUndo(): boolean;
25
18
  destroy(): void;
26
19
  get(key?: undefined): TStorage;
@@ -29,26 +22,14 @@ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> e
29
22
  toJson(): InferCrdtJson<TStorage>;
30
23
  toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
31
24
  isEmpty(): boolean;
32
- /**
33
- * TODO
34
- * @description This method is not yet supported for loro
35
- */
36
25
  redo(): this;
37
26
  subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
38
- /**
39
- * TODO
40
- * @description This method doesn't do anything yet.
41
- */
42
27
  track(): this;
43
28
  /**
44
- * TODO
45
- * @description This method doesn't do anything yet. The callback will still be executed.
29
+ * @description Unlike Yjs, this method is required to be called after each loro operation.
30
+ * @date January 12, 2025
46
31
  */
47
32
  transact(fn: () => void): this;
48
- /**
49
- * TODO
50
- * @description This method is not yet supported for loro
51
- */
52
33
  undo(): this;
53
34
  }
54
35
 
@@ -67,10 +48,14 @@ declare const list: <T extends unknown>(value?: T[] | readonly T[]) => LoroType<
67
48
 
68
49
  declare const map: <T extends unknown>(value?: readonly (readonly [key: string, value: T])[]) => LoroType<LoroMap<Record<string, T>>, Record<string, T>>;
69
50
 
51
+ declare const movableList: <T extends unknown>(value?: T[] | readonly T[]) => LoroType<LoroMovableList<T>, T[]>;
52
+
70
53
  declare const object: <T extends Record<string, any>>(value: T) => LoroType<LoroMap<T>, T>;
71
54
 
72
55
  declare const text: (value?: string) => LoroType<LoroText, string>;
73
56
 
57
+ declare const tree: <T extends Record<string, unknown>>() => LoroTree<T>;
58
+
74
59
  declare const kind: "loro";
75
60
 
76
61
  type loro_CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> = CrdtLoroDoc<TStorage>;
@@ -80,10 +65,12 @@ declare const loro_doc: typeof doc;
80
65
  declare const loro_kind: typeof kind;
81
66
  declare const loro_list: typeof list;
82
67
  declare const loro_map: typeof map;
68
+ declare const loro_movableList: typeof movableList;
83
69
  declare const loro_object: typeof object;
84
70
  declare const loro_text: typeof text;
71
+ declare const loro_tree: typeof tree;
85
72
  declare namespace loro {
86
- export { loro_CrdtLoroDoc as CrdtLoroDoc, type loro_LoroType as LoroType, loro_doc as doc, loro_kind as kind, loro_list as list, loro_map as map, loro_object as object, loro_text as text };
73
+ export { loro_CrdtLoroDoc as CrdtLoroDoc, type loro_LoroType as LoroType, loro_doc as doc, loro_kind as kind, loro_list as list, loro_map as map, loro_movableList as movableList, loro_object as object, loro_text as text, loro_tree as tree };
87
74
  }
88
75
 
89
76
  export { loro };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CrdtType, AbstractCrdtDoc, DocApplyEncodedStateParams, InferCrdtJson, DocSubscribeCallbackParams, AbstractCrdtDocFactory } from '@pluv/crdt';
2
- import { LoroDoc, LoroList, LoroMap, LoroText } from 'loro-crdt';
2
+ import { LoroDoc, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree } from 'loro-crdt';
3
3
 
4
4
  type LoroType<TValue extends unknown, TJson extends unknown = any> = TValue & CrdtType<TValue, TJson> & {
5
5
  toJSON?: () => any;
@@ -9,18 +9,11 @@ type LoroType<TValue extends unknown, TJson extends unknown = any> = TValue & Cr
9
9
  declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDoc<TStorage> {
10
10
  value: LoroDoc;
11
11
  private _storage;
12
+ private _undoManager;
12
13
  constructor(value?: TStorage);
13
14
  applyEncodedState(params: DocApplyEncodedStateParams): this;
14
15
  batchApplyEncodedState(updates: readonly (DocApplyEncodedStateParams | string | null | undefined)[]): this;
15
- /**
16
- * TODO
17
- * @description This method is not yet supported for loro
18
- */
19
16
  canRedo(): boolean;
20
- /**
21
- * TODO
22
- * @description This method is not yet supported for loro
23
- */
24
17
  canUndo(): boolean;
25
18
  destroy(): void;
26
19
  get(key?: undefined): TStorage;
@@ -29,26 +22,14 @@ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> e
29
22
  toJson(): InferCrdtJson<TStorage>;
30
23
  toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
31
24
  isEmpty(): boolean;
32
- /**
33
- * TODO
34
- * @description This method is not yet supported for loro
35
- */
36
25
  redo(): this;
37
26
  subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
38
- /**
39
- * TODO
40
- * @description This method doesn't do anything yet.
41
- */
42
27
  track(): this;
43
28
  /**
44
- * TODO
45
- * @description This method doesn't do anything yet. The callback will still be executed.
29
+ * @description Unlike Yjs, this method is required to be called after each loro operation.
30
+ * @date January 12, 2025
46
31
  */
47
32
  transact(fn: () => void): this;
48
- /**
49
- * TODO
50
- * @description This method is not yet supported for loro
51
- */
52
33
  undo(): this;
53
34
  }
54
35
 
@@ -67,10 +48,14 @@ declare const list: <T extends unknown>(value?: T[] | readonly T[]) => LoroType<
67
48
 
68
49
  declare const map: <T extends unknown>(value?: readonly (readonly [key: string, value: T])[]) => LoroType<LoroMap<Record<string, T>>, Record<string, T>>;
69
50
 
51
+ declare const movableList: <T extends unknown>(value?: T[] | readonly T[]) => LoroType<LoroMovableList<T>, T[]>;
52
+
70
53
  declare const object: <T extends Record<string, any>>(value: T) => LoroType<LoroMap<T>, T>;
71
54
 
72
55
  declare const text: (value?: string) => LoroType<LoroText, string>;
73
56
 
57
+ declare const tree: <T extends Record<string, unknown>>() => LoroTree<T>;
58
+
74
59
  declare const kind: "loro";
75
60
 
76
61
  type loro_CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> = CrdtLoroDoc<TStorage>;
@@ -80,10 +65,12 @@ declare const loro_doc: typeof doc;
80
65
  declare const loro_kind: typeof kind;
81
66
  declare const loro_list: typeof list;
82
67
  declare const loro_map: typeof map;
68
+ declare const loro_movableList: typeof movableList;
83
69
  declare const loro_object: typeof object;
84
70
  declare const loro_text: typeof text;
71
+ declare const loro_tree: typeof tree;
85
72
  declare namespace loro {
86
- export { loro_CrdtLoroDoc as CrdtLoroDoc, type loro_LoroType as LoroType, loro_doc as doc, loro_kind as kind, loro_list as list, loro_map as map, loro_object as object, loro_text as text };
73
+ export { loro_CrdtLoroDoc as CrdtLoroDoc, type loro_LoroType as LoroType, loro_doc as doc, loro_kind as kind, loro_list as list, loro_map as map, loro_movableList as movableList, loro_object as object, loro_text as text, loro_tree as tree };
87
74
  }
88
75
 
89
76
  export { loro };
package/dist/index.js CHANGED
@@ -49,18 +49,23 @@ __export(loro_exports, {
49
49
  kind: () => kind,
50
50
  list: () => list,
51
51
  map: () => map,
52
+ movableList: () => movableList,
52
53
  object: () => object,
53
- text: () => text
54
+ text: () => text,
55
+ tree: () => tree
54
56
  });
55
57
 
56
58
  // src/doc/CrdtLoroDoc.ts
57
59
  var import_crdt = require("@pluv/crdt");
58
60
  var import_js_base64 = require("js-base64");
59
61
  var import_loro_crdt = require("loro-crdt");
62
+ var MAX_UNDO_STEPS = 100;
63
+ var MERGE_INTERVAL_MS = 1e3;
60
64
  var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
61
65
  constructor(value = {}) {
62
66
  super();
63
67
  this.value = new import_loro_crdt.LoroDoc();
68
+ this._undoManager = null;
64
69
  this._storage = Object.entries(value).reduce((acc, [key, node]) => {
65
70
  if (node instanceof import_loro_crdt.LoroList) {
66
71
  const container = this.value.getList(key);
@@ -112,22 +117,16 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
112
117
  update && this.value.import(update);
113
118
  return this;
114
119
  }
115
- this.value.importUpdateBatch(_updates);
120
+ this.value.importBatch(_updates);
116
121
  return this;
117
122
  }
118
- /**
119
- * TODO
120
- * @description This method is not yet supported for loro
121
- */
122
123
  canRedo() {
123
- return false;
124
+ if (!this._undoManager) return false;
125
+ return this._undoManager.canRedo();
124
126
  }
125
- /**
126
- * TODO
127
- * @description This method is not yet supported for loro
128
- */
129
127
  canUndo() {
130
- return false;
128
+ if (!this._undoManager) return false;
129
+ return this._undoManager.canUndo();
131
130
  }
132
131
  destroy() {
133
132
  return;
@@ -155,12 +154,10 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
155
154
  const serialized = this.value.toJSON();
156
155
  return !serialized || !Object.keys(serialized).length;
157
156
  }
158
- /**
159
- * TODO
160
- * @description This method is not yet supported for loro
161
- */
162
157
  redo() {
163
- throw new Error("This is not yet supported");
158
+ var _a;
159
+ (_a = this._undoManager) == null ? void 0 : _a.redo();
160
+ return this;
164
161
  }
165
162
  subscribe(listener) {
166
163
  const fn = (event) => {
@@ -185,28 +182,31 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
185
182
  );
186
183
  return unsubcribeAll;
187
184
  }
188
- /**
189
- * TODO
190
- * @description This method doesn't do anything yet.
191
- */
192
185
  track() {
186
+ if (this._undoManager) {
187
+ this._undoManager.clear();
188
+ this._undoManager.free();
189
+ this._undoManager = null;
190
+ }
191
+ this._undoManager = new import_loro_crdt.UndoManager(this.value, {
192
+ maxUndoSteps: MAX_UNDO_STEPS,
193
+ mergeInterval: MERGE_INTERVAL_MS
194
+ });
193
195
  return this;
194
196
  }
195
197
  /**
196
- * TODO
197
- * @description This method doesn't do anything yet. The callback will still be executed.
198
+ * @description Unlike Yjs, this method is required to be called after each loro operation.
199
+ * @date January 12, 2025
198
200
  */
199
201
  transact(fn) {
200
202
  fn();
201
203
  this.value.commit();
202
204
  return this;
203
205
  }
204
- /**
205
- * TODO
206
- * @description This method is not yet supported for loro
207
- */
208
206
  undo() {
209
- throw new Error("This is not yet supported");
207
+ var _a;
208
+ (_a = this._undoManager) == null ? void 0 : _a.undo();
209
+ return this;
210
210
  }
211
211
  };
212
212
 
@@ -272,24 +272,41 @@ var map = (value = []) => {
272
272
  return container;
273
273
  };
274
274
 
275
- // src/object.ts
275
+ // src/movableList.ts
276
276
  var import_loro_crdt5 = require("loro-crdt");
277
+ var movableList = (value = []) => {
278
+ const container = new import_loro_crdt5.LoroMovableList();
279
+ value.forEach((item, i) => {
280
+ (0, import_loro_crdt5.isContainer)(item) ? container.insertContainer(i, item) : container.insert(i, item);
281
+ });
282
+ return container;
283
+ };
284
+
285
+ // src/object.ts
286
+ var import_loro_crdt6 = require("loro-crdt");
277
287
  var object = (value) => {
278
- const container = new import_loro_crdt5.LoroMap();
288
+ const container = new import_loro_crdt6.LoroMap();
279
289
  Object.entries(value).forEach(([key, item]) => {
280
- (0, import_loro_crdt5.isContainer)(item) ? container.setContainer(key, item) : container.set(key, item);
290
+ (0, import_loro_crdt6.isContainer)(item) ? container.setContainer(key, item) : container.set(key, item);
281
291
  });
282
292
  return container;
283
293
  };
284
294
 
285
295
  // src/text.ts
286
- var import_loro_crdt6 = require("loro-crdt");
296
+ var import_loro_crdt7 = require("loro-crdt");
287
297
  var text = (value = "") => {
288
- const container = new import_loro_crdt6.LoroText();
298
+ const container = new import_loro_crdt7.LoroText();
289
299
  container.insert(0, value);
290
300
  return container;
291
301
  };
292
302
 
303
+ // src/tree.ts
304
+ var import_loro_crdt8 = require("loro-crdt");
305
+ var tree = () => {
306
+ const container = new import_loro_crdt8.LoroTree();
307
+ return container;
308
+ };
309
+
293
310
  // src/loro.ts
294
311
  var kind = "loro";
295
312
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.mjs CHANGED
@@ -30,18 +30,23 @@ __export(loro_exports, {
30
30
  kind: () => kind,
31
31
  list: () => list,
32
32
  map: () => map,
33
+ movableList: () => movableList,
33
34
  object: () => object,
34
- text: () => text
35
+ text: () => text,
36
+ tree: () => tree
35
37
  });
36
38
 
37
39
  // src/doc/CrdtLoroDoc.ts
38
40
  import { AbstractCrdtDoc } from "@pluv/crdt";
39
41
  import { fromUint8Array, toUint8Array } from "js-base64";
40
- import { LoroDoc, LoroList, LoroMap, LoroText, isContainer } from "loro-crdt";
42
+ import { LoroDoc, LoroList, LoroMap, LoroText, UndoManager, isContainer } from "loro-crdt";
43
+ var MAX_UNDO_STEPS = 100;
44
+ var MERGE_INTERVAL_MS = 1e3;
41
45
  var CrdtLoroDoc = class extends AbstractCrdtDoc {
42
46
  constructor(value = {}) {
43
47
  super();
44
48
  this.value = new LoroDoc();
49
+ this._undoManager = null;
45
50
  this._storage = Object.entries(value).reduce((acc, [key, node]) => {
46
51
  if (node instanceof LoroList) {
47
52
  const container = this.value.getList(key);
@@ -93,22 +98,16 @@ var CrdtLoroDoc = class extends AbstractCrdtDoc {
93
98
  update && this.value.import(update);
94
99
  return this;
95
100
  }
96
- this.value.importUpdateBatch(_updates);
101
+ this.value.importBatch(_updates);
97
102
  return this;
98
103
  }
99
- /**
100
- * TODO
101
- * @description This method is not yet supported for loro
102
- */
103
104
  canRedo() {
104
- return false;
105
+ if (!this._undoManager) return false;
106
+ return this._undoManager.canRedo();
105
107
  }
106
- /**
107
- * TODO
108
- * @description This method is not yet supported for loro
109
- */
110
108
  canUndo() {
111
- return false;
109
+ if (!this._undoManager) return false;
110
+ return this._undoManager.canUndo();
112
111
  }
113
112
  destroy() {
114
113
  return;
@@ -136,12 +135,10 @@ var CrdtLoroDoc = class extends AbstractCrdtDoc {
136
135
  const serialized = this.value.toJSON();
137
136
  return !serialized || !Object.keys(serialized).length;
138
137
  }
139
- /**
140
- * TODO
141
- * @description This method is not yet supported for loro
142
- */
143
138
  redo() {
144
- throw new Error("This is not yet supported");
139
+ var _a;
140
+ (_a = this._undoManager) == null ? void 0 : _a.redo();
141
+ return this;
145
142
  }
146
143
  subscribe(listener) {
147
144
  const fn = (event) => {
@@ -166,28 +163,31 @@ var CrdtLoroDoc = class extends AbstractCrdtDoc {
166
163
  );
167
164
  return unsubcribeAll;
168
165
  }
169
- /**
170
- * TODO
171
- * @description This method doesn't do anything yet.
172
- */
173
166
  track() {
167
+ if (this._undoManager) {
168
+ this._undoManager.clear();
169
+ this._undoManager.free();
170
+ this._undoManager = null;
171
+ }
172
+ this._undoManager = new UndoManager(this.value, {
173
+ maxUndoSteps: MAX_UNDO_STEPS,
174
+ mergeInterval: MERGE_INTERVAL_MS
175
+ });
174
176
  return this;
175
177
  }
176
178
  /**
177
- * TODO
178
- * @description This method doesn't do anything yet. The callback will still be executed.
179
+ * @description Unlike Yjs, this method is required to be called after each loro operation.
180
+ * @date January 12, 2025
179
181
  */
180
182
  transact(fn) {
181
183
  fn();
182
184
  this.value.commit();
183
185
  return this;
184
186
  }
185
- /**
186
- * TODO
187
- * @description This method is not yet supported for loro
188
- */
189
187
  undo() {
190
- throw new Error("This is not yet supported");
188
+ var _a;
189
+ (_a = this._undoManager) == null ? void 0 : _a.undo();
190
+ return this;
191
191
  }
192
192
  };
193
193
 
@@ -253,12 +253,22 @@ var map = (value = []) => {
253
253
  return container;
254
254
  };
255
255
 
256
+ // src/movableList.ts
257
+ import { isContainer as isContainer4, LoroMovableList } from "loro-crdt";
258
+ var movableList = (value = []) => {
259
+ const container = new LoroMovableList();
260
+ value.forEach((item, i) => {
261
+ isContainer4(item) ? container.insertContainer(i, item) : container.insert(i, item);
262
+ });
263
+ return container;
264
+ };
265
+
256
266
  // src/object.ts
257
- import { LoroMap as LoroMap4, isContainer as isContainer4 } from "loro-crdt";
267
+ import { LoroMap as LoroMap4, isContainer as isContainer5 } from "loro-crdt";
258
268
  var object = (value) => {
259
269
  const container = new LoroMap4();
260
270
  Object.entries(value).forEach(([key, item]) => {
261
- isContainer4(item) ? container.setContainer(key, item) : container.set(key, item);
271
+ isContainer5(item) ? container.setContainer(key, item) : container.set(key, item);
262
272
  });
263
273
  return container;
264
274
  };
@@ -271,6 +281,13 @@ var text = (value = "") => {
271
281
  return container;
272
282
  };
273
283
 
284
+ // src/tree.ts
285
+ import { LoroTree } from "loro-crdt";
286
+ var tree = () => {
287
+ const container = new LoroTree();
288
+ return container;
289
+ };
290
+
274
291
  // src/loro.ts
275
292
  var kind = "loro";
276
293
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/crdt-loro",
3
- "version": "0.34.1",
3
+ "version": "0.35.1",
4
4
  "description": "loro for @pluv/io",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,19 +18,19 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "js-base64": "^3.7.7",
21
- "@pluv/crdt": "^0.34.1",
22
- "@pluv/types": "^0.34.1"
21
+ "@pluv/crdt": "^0.35.1",
22
+ "@pluv/types": "^0.35.1"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "loro-crdt": "^1.0.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "eslint": "^8.57.1",
29
- "loro-crdt": "^1.2.4",
29
+ "loro-crdt": "^1.3.1",
30
30
  "tsup": "^8.3.5",
31
- "typescript": "^5.7.2",
32
- "@pluv/tsconfig": "^0.34.1",
33
- "eslint-config-pluv": "^0.34.1"
31
+ "typescript": "^5.7.3",
32
+ "@pluv/tsconfig": "^0.35.1",
33
+ "eslint-config-pluv": "^0.35.1"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,14 +1,18 @@
1
1
  import type { DocApplyEncodedStateParams, DocSubscribeCallbackParams, InferCrdtJson } from "@pluv/crdt";
2
2
  import { AbstractCrdtDoc } from "@pluv/crdt";
3
3
  import { fromUint8Array, toUint8Array } from "js-base64";
4
- import type { Container, LoroEventBatch } from "loro-crdt";
5
- import { LoroDoc, LoroList, LoroMap, LoroText, isContainer } from "loro-crdt";
4
+ import type { Container } from "loro-crdt";
5
+ import { LoroDoc, LoroEventBatch, LoroList, LoroMap, LoroText, UndoManager, isContainer } from "loro-crdt";
6
6
  import type { LoroType } from "../types";
7
7
 
8
+ const MAX_UNDO_STEPS = 100;
9
+ const MERGE_INTERVAL_MS = 1_000;
10
+
8
11
  export class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDoc<TStorage> {
9
12
  public value: LoroDoc = new LoroDoc();
10
13
 
11
14
  private _storage: TStorage;
15
+ private _undoManager: UndoManager | null = null;
12
16
 
13
17
  constructor(value: TStorage = {} as TStorage) {
14
18
  super();
@@ -89,25 +93,21 @@ export class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> ex
89
93
  return this;
90
94
  }
91
95
 
92
- this.value.importUpdateBatch(_updates);
96
+ this.value.importBatch(_updates);
93
97
 
94
98
  return this;
95
99
  }
96
100
 
97
- /**
98
- * TODO
99
- * @description This method is not yet supported for loro
100
- */
101
101
  public canRedo(): boolean {
102
- return false;
102
+ if (!this._undoManager) return false;
103
+
104
+ return this._undoManager.canRedo();
103
105
  }
104
106
 
105
- /**
106
- * TODO
107
- * @description This method is not yet supported for loro
108
- */
109
107
  public canUndo(): boolean {
110
- return false;
108
+ if (!this._undoManager) return false;
109
+
110
+ return this._undoManager.canUndo();
111
111
  }
112
112
 
113
113
  public destroy(): void {
@@ -150,12 +150,10 @@ export class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> ex
150
150
  return !serialized || !Object.keys(serialized).length;
151
151
  }
152
152
 
153
- /**
154
- * TODO
155
- * @description This method is not yet supported for loro
156
- */
157
153
  public redo(): this {
158
- throw new Error("This is not yet supported");
154
+ this._undoManager?.redo();
155
+
156
+ return this;
159
157
  }
160
158
 
161
159
  public subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void {
@@ -186,17 +184,25 @@ export class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> ex
186
184
  return unsubcribeAll;
187
185
  }
188
186
 
189
- /**
190
- * TODO
191
- * @description This method doesn't do anything yet.
192
- */
193
187
  public track(): this {
188
+ if (this._undoManager) {
189
+ this._undoManager.clear();
190
+ this._undoManager.free();
191
+
192
+ this._undoManager = null;
193
+ }
194
+
195
+ this._undoManager = new UndoManager(this.value, {
196
+ maxUndoSteps: MAX_UNDO_STEPS,
197
+ mergeInterval: MERGE_INTERVAL_MS,
198
+ });
199
+
194
200
  return this;
195
201
  }
196
202
 
197
203
  /**
198
- * TODO
199
- * @description This method doesn't do anything yet. The callback will still be executed.
204
+ * @description Unlike Yjs, this method is required to be called after each loro operation.
205
+ * @date January 12, 2025
200
206
  */
201
207
  public transact(fn: () => void): this {
202
208
  fn();
@@ -206,11 +212,9 @@ export class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> ex
206
212
  return this;
207
213
  }
208
214
 
209
- /**
210
- * TODO
211
- * @description This method is not yet supported for loro
212
- */
213
215
  public undo(): this {
214
- throw new Error("This is not yet supported");
216
+ this._undoManager?.undo();
217
+
218
+ return this;
215
219
  }
216
220
  }
package/src/loro.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export { CrdtLoroDoc, doc } from "./doc";
2
2
  export { list } from "./list";
3
3
  export { map } from "./map";
4
+ export { movableList } from "./movableList";
4
5
  export { object } from "./object";
5
6
  export { text } from "./text";
7
+ export { tree } from "./tree";
6
8
  export type { LoroType } from "./types";
7
9
  export const kind = "loro" as const;
@@ -0,0 +1,13 @@
1
+ import type { Container } from "loro-crdt";
2
+ import { isContainer, LoroMovableList } from "loro-crdt";
3
+ import type { LoroType } from "./types";
4
+
5
+ export const movableList = <T extends unknown>(value: T[] | readonly T[] = []) => {
6
+ const container = new LoroMovableList();
7
+
8
+ value.forEach((item, i) => {
9
+ isContainer(item) ? container.insertContainer(i, item) : container.insert(i, item as Exclude<T, Container>);
10
+ });
11
+
12
+ return container as unknown as LoroType<LoroMovableList<T>, T[]>;
13
+ };
package/src/tree.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { LoroTree } from "loro-crdt";
2
+
3
+ export const tree = <T extends Record<string, unknown>>() => {
4
+ const container = new LoroTree<T>();
5
+
6
+ return container;
7
+ };