@pluv/crdt-loro 1.0.1 → 2.0.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,5 +1,5 @@
1
1
 
2
- > @pluv/crdt-loro@1.0.1 build /home/runner/work/pluv/pluv/packages/crdt-loro
2
+ > @pluv/crdt-loro@2.0.0 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.73 KB
12
- ESM ⚡️ Build success in 83ms
13
- CJS dist/index.js 9.78 KB
14
- CJS ⚡️ Build success in 82ms
11
+ ESM dist/index.mjs 14.35 KB
12
+ ESM ⚡️ Build success in 71ms
13
+ CJS dist/index.js 15.55 KB
14
+ CJS ⚡️ Build success in 77ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 2550ms
17
- DTS dist/index.d.mts 3.46 KB
18
- DTS dist/index.d.ts 3.46 KB
16
+ DTS ⚡️ Build success in 4019ms
17
+ DTS dist/index.d.mts 4.57 KB
18
+ DTS dist/index.d.ts 4.57 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @pluv/crdt-loro
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0b234d1: **Deprecated** Declaring top-level types on your CRDT storage via the `loro` utility (e.g. `loro.list`) is now deprecated, to be removed in the next major release (v2). Continuing to use the `loro` utility to declare top-level types will log a warning to your console while in development mode (i.e. when `process.env.NODE_ENV === "development"`). To declare top-level types, you should now use the builder type exposed in the storage factory function. See the example below:
8
+
9
+ ```ts
10
+ import { loro } from "@pluv/crdt-loro";
11
+
12
+ // Before
13
+ loro.doc(() => ({
14
+ // Using the `loro` utility (in this case `loro.map`) when declaring
15
+ // top-level types is now deprecated
16
+ topType: loro.map({
17
+ key1: loro.text(""),
18
+ key2: loro.text(""),
19
+ }),
20
+ }));
21
+
22
+ // After
23
+ loro.doc((t) => ({
24
+ // Top-level properties must now use the builder (in this case `t`).
25
+ // This effectively calls `loro.getMap("topType")` to instantiate the type on
26
+ // the root document.
27
+ // This simply returns the native Loro container type (e.g. LoroMap), which
28
+ // allows you operate on your loro container types in a more native way.
29
+ topType: t.map("topType", {
30
+ // Declaring nested types should continue to use the `loro` utilities
31
+ // you've used before
32
+ key1: loro.text(""),
33
+ key2: loro.text(""),
34
+ }),
35
+ }));
36
+ ```
37
+
38
+ The functions on the builder type calls the native loro container instantiating methods and returns the native types (e.g. `t.map("topType")` is simply just a `LoroDoc.getMap("topType")`) call under the hood. This allows you to declare your type in a more loro-native and predictable way, and enables declaring more complex initial storage states if needed.
39
+
40
+ ### Patch Changes
41
+
42
+ - Updated dependencies [047a1d8]
43
+ - @pluv/types@2.0.0
44
+ - @pluv/crdt@2.0.0
45
+
46
+ ## 1.0.2
47
+
48
+ ### Patch Changes
49
+
50
+ - @pluv/crdt@1.0.2
51
+ - @pluv/types@1.0.2
52
+
3
53
  ## 1.0.1
4
54
 
5
55
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1,16 +1,31 @@
1
- import { CrdtType, AbstractCrdtDoc, DocApplyEncodedStateParams, DocBatchApplyEncodedStateParams, InferCrdtJson, DocSubscribeCallbackParams, AbstractCrdtDocFactory } from '@pluv/crdt';
2
- import { LoroDoc, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree } from 'loro-crdt';
1
+ import * as loro_crdt from 'loro-crdt';
2
+ import { LoroCounter, LoroDoc, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree } from 'loro-crdt';
3
+ import { CrdtType, DocApplyEncodedStateParams, DocBatchApplyEncodedStateParams, InferCrdtJson, DocSubscribeCallbackParams, AbstractCrdtDocFactory } from '@pluv/crdt';
4
+ import { CrdtDocLike } from '@pluv/types';
5
+
6
+ declare const counter: () => LoroCounter;
3
7
 
4
8
  type LoroType<TValue extends unknown, TJson extends unknown = any> = TValue & CrdtType<TValue, TJson> & {
5
9
  toJSON?: () => any;
6
10
  toString?: () => string;
7
11
  };
8
12
 
9
- declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDoc<TStorage> {
13
+ type LoroBuilder = ReturnType<typeof builder>;
14
+ declare const builder: (doc: LoroDoc) => {
15
+ counter(name: string): LoroCounter;
16
+ list<T extends unknown>(name: string, value?: T[] | readonly T[]): LoroType<LoroList<T>, T[]>;
17
+ map<T extends Record<string, unknown>>(name: string, value?: T): LoroType<LoroMap<T>, T>;
18
+ moveableList<T extends unknown>(name: string, value?: T[] | readonly T[]): LoroType<LoroMovableList<T>, T[]>;
19
+ text(name: string, value?: string): LoroType<LoroText, string>;
20
+ tree(name: string): loro_crdt.LoroTree<Record<string, unknown>>;
21
+ };
22
+
23
+ type CrdtLoroDocParams<TStorage extends Record<string, LoroType<any, any>>> = (builder: LoroBuilder) => TStorage;
24
+ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> implements CrdtDocLike<TStorage> {
10
25
  value: LoroDoc;
11
26
  private _storage;
12
27
  private _undoManager;
13
- constructor(value?: TStorage);
28
+ constructor(params?: CrdtLoroDocParams<TStorage>);
14
29
  applyEncodedState(params: DocApplyEncodedStateParams): this;
15
30
  batchApplyEncodedState(params: DocBatchApplyEncodedStateParams): this;
16
31
  canRedo(): boolean;
@@ -22,7 +37,7 @@ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> e
22
37
  toJson(): InferCrdtJson<TStorage>;
23
38
  toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
24
39
  isEmpty(): boolean;
25
- rebuildStorage(): this;
40
+ rebuildStorage(reference: TStorage): this;
26
41
  redo(): this;
27
42
  subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
28
43
  track(): this;
@@ -32,17 +47,18 @@ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> e
32
47
  */
33
48
  transact(fn: () => void): this;
34
49
  undo(): this;
50
+ private _warn;
35
51
  }
36
52
 
37
53
  declare class CrdtLoroDocFactory<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDocFactory<TStorage> {
38
- private _initialStorage;
39
- constructor(initialStorage?: () => TStorage);
54
+ readonly _initialStorage: (builder: LoroBuilder) => TStorage;
55
+ constructor(initialStorage?: (builder: LoroBuilder) => TStorage);
40
56
  getEmpty(): CrdtLoroDoc<TStorage>;
41
- getFactory(initialStorage?: (() => TStorage) | undefined): CrdtLoroDocFactory<TStorage>;
42
- getInitialized(initialStorage?: () => TStorage): CrdtLoroDoc<TStorage>;
57
+ getFactory(initialStorage?: ((builder: LoroBuilder) => TStorage) | undefined): CrdtLoroDocFactory<TStorage>;
58
+ getInitialized(initialStorage?: (builder: LoroBuilder) => TStorage): CrdtLoroDoc<TStorage>;
43
59
  }
44
60
 
45
- declare const doc: <TStorage extends Record<string, LoroType<any, any>>>(value?: () => TStorage) => CrdtLoroDocFactory<TStorage>;
61
+ declare const doc: <TStorage extends Record<string, LoroType<any, any>>>(value?: (builder: LoroBuilder) => TStorage) => CrdtLoroDocFactory<TStorage>;
46
62
 
47
63
  declare const list: <T extends unknown>(value?: T[] | readonly T[]) => LoroType<LoroList<T>, T[]>;
48
64
 
@@ -60,7 +76,9 @@ declare const kind: "loro";
60
76
 
61
77
  type loro_CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> = CrdtLoroDoc<TStorage>;
62
78
  declare const loro_CrdtLoroDoc: typeof CrdtLoroDoc;
79
+ type loro_LoroBuilder = LoroBuilder;
63
80
  type loro_LoroType<TValue extends unknown, TJson extends unknown = any> = LoroType<TValue, TJson>;
81
+ declare const loro_counter: typeof counter;
64
82
  declare const loro_doc: typeof doc;
65
83
  declare const loro_kind: typeof kind;
66
84
  declare const loro_list: typeof list;
@@ -70,7 +88,7 @@ declare const loro_object: typeof object;
70
88
  declare const loro_text: typeof text;
71
89
  declare const loro_tree: typeof tree;
72
90
  declare namespace loro {
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 };
91
+ export { loro_CrdtLoroDoc as CrdtLoroDoc, type loro_LoroBuilder as LoroBuilder, type loro_LoroType as LoroType, loro_counter as counter, 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 };
74
92
  }
75
93
 
76
94
  export { loro };
package/dist/index.d.ts CHANGED
@@ -1,16 +1,31 @@
1
- import { CrdtType, AbstractCrdtDoc, DocApplyEncodedStateParams, DocBatchApplyEncodedStateParams, InferCrdtJson, DocSubscribeCallbackParams, AbstractCrdtDocFactory } from '@pluv/crdt';
2
- import { LoroDoc, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree } from 'loro-crdt';
1
+ import * as loro_crdt from 'loro-crdt';
2
+ import { LoroCounter, LoroDoc, LoroList, LoroMap, LoroMovableList, LoroText, LoroTree } from 'loro-crdt';
3
+ import { CrdtType, DocApplyEncodedStateParams, DocBatchApplyEncodedStateParams, InferCrdtJson, DocSubscribeCallbackParams, AbstractCrdtDocFactory } from '@pluv/crdt';
4
+ import { CrdtDocLike } from '@pluv/types';
5
+
6
+ declare const counter: () => LoroCounter;
3
7
 
4
8
  type LoroType<TValue extends unknown, TJson extends unknown = any> = TValue & CrdtType<TValue, TJson> & {
5
9
  toJSON?: () => any;
6
10
  toString?: () => string;
7
11
  };
8
12
 
9
- declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDoc<TStorage> {
13
+ type LoroBuilder = ReturnType<typeof builder>;
14
+ declare const builder: (doc: LoroDoc) => {
15
+ counter(name: string): LoroCounter;
16
+ list<T extends unknown>(name: string, value?: T[] | readonly T[]): LoroType<LoroList<T>, T[]>;
17
+ map<T extends Record<string, unknown>>(name: string, value?: T): LoroType<LoroMap<T>, T>;
18
+ moveableList<T extends unknown>(name: string, value?: T[] | readonly T[]): LoroType<LoroMovableList<T>, T[]>;
19
+ text(name: string, value?: string): LoroType<LoroText, string>;
20
+ tree(name: string): loro_crdt.LoroTree<Record<string, unknown>>;
21
+ };
22
+
23
+ type CrdtLoroDocParams<TStorage extends Record<string, LoroType<any, any>>> = (builder: LoroBuilder) => TStorage;
24
+ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> implements CrdtDocLike<TStorage> {
10
25
  value: LoroDoc;
11
26
  private _storage;
12
27
  private _undoManager;
13
- constructor(value?: TStorage);
28
+ constructor(params?: CrdtLoroDocParams<TStorage>);
14
29
  applyEncodedState(params: DocApplyEncodedStateParams): this;
15
30
  batchApplyEncodedState(params: DocBatchApplyEncodedStateParams): this;
16
31
  canRedo(): boolean;
@@ -22,7 +37,7 @@ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> e
22
37
  toJson(): InferCrdtJson<TStorage>;
23
38
  toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
24
39
  isEmpty(): boolean;
25
- rebuildStorage(): this;
40
+ rebuildStorage(reference: TStorage): this;
26
41
  redo(): this;
27
42
  subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
28
43
  track(): this;
@@ -32,17 +47,18 @@ declare class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> e
32
47
  */
33
48
  transact(fn: () => void): this;
34
49
  undo(): this;
50
+ private _warn;
35
51
  }
36
52
 
37
53
  declare class CrdtLoroDocFactory<TStorage extends Record<string, LoroType<any, any>>> extends AbstractCrdtDocFactory<TStorage> {
38
- private _initialStorage;
39
- constructor(initialStorage?: () => TStorage);
54
+ readonly _initialStorage: (builder: LoroBuilder) => TStorage;
55
+ constructor(initialStorage?: (builder: LoroBuilder) => TStorage);
40
56
  getEmpty(): CrdtLoroDoc<TStorage>;
41
- getFactory(initialStorage?: (() => TStorage) | undefined): CrdtLoroDocFactory<TStorage>;
42
- getInitialized(initialStorage?: () => TStorage): CrdtLoroDoc<TStorage>;
57
+ getFactory(initialStorage?: ((builder: LoroBuilder) => TStorage) | undefined): CrdtLoroDocFactory<TStorage>;
58
+ getInitialized(initialStorage?: (builder: LoroBuilder) => TStorage): CrdtLoroDoc<TStorage>;
43
59
  }
44
60
 
45
- declare const doc: <TStorage extends Record<string, LoroType<any, any>>>(value?: () => TStorage) => CrdtLoroDocFactory<TStorage>;
61
+ declare const doc: <TStorage extends Record<string, LoroType<any, any>>>(value?: (builder: LoroBuilder) => TStorage) => CrdtLoroDocFactory<TStorage>;
46
62
 
47
63
  declare const list: <T extends unknown>(value?: T[] | readonly T[]) => LoroType<LoroList<T>, T[]>;
48
64
 
@@ -60,7 +76,9 @@ declare const kind: "loro";
60
76
 
61
77
  type loro_CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>> = CrdtLoroDoc<TStorage>;
62
78
  declare const loro_CrdtLoroDoc: typeof CrdtLoroDoc;
79
+ type loro_LoroBuilder = LoroBuilder;
63
80
  type loro_LoroType<TValue extends unknown, TJson extends unknown = any> = LoroType<TValue, TJson>;
81
+ declare const loro_counter: typeof counter;
64
82
  declare const loro_doc: typeof doc;
65
83
  declare const loro_kind: typeof kind;
66
84
  declare const loro_list: typeof list;
@@ -70,7 +88,7 @@ declare const loro_object: typeof object;
70
88
  declare const loro_text: typeof text;
71
89
  declare const loro_tree: typeof tree;
72
90
  declare namespace loro {
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 };
91
+ export { loro_CrdtLoroDoc as CrdtLoroDoc, type loro_LoroBuilder as LoroBuilder, type loro_LoroType as LoroType, loro_counter as counter, 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 };
74
92
  }
75
93
 
76
94
  export { loro };
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ module.exports = __toCommonJS(index_exports);
45
45
  var loro_exports = {};
46
46
  __export(loro_exports, {
47
47
  CrdtLoroDoc: () => CrdtLoroDoc,
48
+ counter: () => counter,
48
49
  doc: () => doc,
49
50
  kind: () => kind,
50
51
  list: () => list,
@@ -55,39 +56,151 @@ __export(loro_exports, {
55
56
  tree: () => tree
56
57
  });
57
58
 
59
+ // src/counter.ts
60
+ var import_loro_crdt = require("loro-crdt");
61
+ var counter = () => {
62
+ const counter2 = new import_loro_crdt.LoroCounter();
63
+ return counter2;
64
+ };
65
+
58
66
  // src/doc/CrdtLoroDoc.ts
59
- var import_crdt = require("@pluv/crdt");
60
67
  var import_js_base64 = require("js-base64");
61
- var import_loro_crdt = require("loro-crdt");
68
+ var import_loro_crdt3 = require("loro-crdt");
69
+
70
+ // src/utils/oneLine.ts
71
+ var oneLine = (strings, ...values) => {
72
+ return strings.map((str) => str.replace(/\s+/g, " ").trim()).reduce((acc, str, i) => acc + str + (i < values.length ? values[i] : ""), "").replace(/\s+/g, " ").trim();
73
+ };
74
+
75
+ // src/doc/builder.ts
76
+ var import_loro_crdt2 = require("loro-crdt");
77
+ var builder = (doc2) => {
78
+ return {
79
+ counter(name) {
80
+ return doc2.getCounter(name);
81
+ },
82
+ list(name, value = []) {
83
+ const list2 = doc2.getList(name);
84
+ value.forEach((item, i) => {
85
+ if ((0, import_loro_crdt2.isContainer)(item)) list2.insertContainer(i, item);
86
+ else list2.insert(i, item);
87
+ });
88
+ return list2;
89
+ },
90
+ map(name, value = {}) {
91
+ const container = doc2.getMap(name);
92
+ Object.entries(value).forEach(([key, item]) => {
93
+ if ((0, import_loro_crdt2.isContainer)(item)) container.setContainer(key, item);
94
+ else container.set(key, item);
95
+ });
96
+ return container;
97
+ },
98
+ moveableList(name, value = []) {
99
+ const list2 = doc2.getMovableList(name);
100
+ value.forEach((item, i) => {
101
+ if ((0, import_loro_crdt2.isContainer)(item)) list2.insertContainer(i, item);
102
+ else list2.insert(i, item);
103
+ });
104
+ return list2;
105
+ },
106
+ text(name, value = "") {
107
+ const text2 = doc2.getText(name);
108
+ text2.insert(0, value);
109
+ return text2;
110
+ },
111
+ tree(name) {
112
+ return doc2.getTree(name);
113
+ }
114
+ };
115
+ };
116
+
117
+ // src/doc/CrdtLoroDoc.ts
62
118
  var MAX_UNDO_STEPS = 100;
63
119
  var MERGE_INTERVAL_MS = 1e3;
64
- var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
65
- constructor(value = {}) {
66
- super();
67
- this.value = new import_loro_crdt.LoroDoc();
120
+ var CrdtLoroDoc = class {
121
+ constructor(params = () => ({})) {
122
+ this.value = new import_loro_crdt3.LoroDoc();
68
123
  this._undoManager = null;
69
- this._storage = Object.entries(value).reduce((acc, [key, node]) => {
70
- if (node instanceof import_loro_crdt.LoroList) {
124
+ const storage = params(builder(this.value));
125
+ const keys = Object.keys(this.value.toJSON()).reduce(
126
+ (set, key) => set.add(key),
127
+ /* @__PURE__ */ new Set()
128
+ );
129
+ this._storage = Object.entries(storage).reduce((acc, [key, node]) => {
130
+ if (keys.has(key)) return __spreadProps(__spreadValues({}, acc), { [key]: node });
131
+ if (node instanceof import_loro_crdt3.LoroCounter) {
132
+ this._warn(oneLine`
133
+ Warning: You are using \`loro.counter\` to declare top-level storage value \`${key}\`.
134
+ Adding top-level values this way has been deprecated, to be removed in v2.
135
+ Please follow the v2 migration guide to declare top-level types correctly:
136
+ https://pluv.io/docs/migration-guides/v2
137
+ `);
138
+ const container = this.value.getCounter(key);
139
+ return __spreadProps(__spreadValues({}, acc), { [key]: container });
140
+ }
141
+ if (node instanceof import_loro_crdt3.LoroList) {
142
+ this._warn(oneLine`
143
+ Warning: You are using \`loro.list\` to declare top-level storage value \`${key}\`.
144
+ Adding top-level values this way has been deprecated, to be removed in v2.
145
+ Please follow the v2 migration guide to declare top-level types correctly:
146
+ https://pluv.io/docs/migration-guides/v2
147
+ `);
71
148
  const container = this.value.getList(key);
72
149
  node.toArray().forEach((item, i) => {
73
- if ((0, import_loro_crdt.isContainer)(item)) container.insertContainer(i, item);
150
+ if ((0, import_loro_crdt3.isContainer)(item)) container.insertContainer(i, item);
74
151
  else container.insert(i, item);
75
152
  });
76
153
  return __spreadProps(__spreadValues({}, acc), { [key]: container });
77
154
  }
78
- if (node instanceof import_loro_crdt.LoroMap) {
155
+ if (node instanceof import_loro_crdt3.LoroMap) {
156
+ this._warn(oneLine`
157
+ Warning: You are using \`loro.map\` to declare top-level storage value \`${key}\`.
158
+ Adding top-level values this way has been deprecated, to be removed in v2.
159
+ Please follow the v2 migration guide to declare top-level types correctly:
160
+ https://pluv.io/docs/migration-guides/v2
161
+ `);
79
162
  const container = this.value.getMap(key);
80
163
  container.entries().forEach(([key2, item]) => {
81
- if ((0, import_loro_crdt.isContainer)(item)) container.setContainer(key2, item);
164
+ if ((0, import_loro_crdt3.isContainer)(item)) container.setContainer(key2, item);
82
165
  else container.set(key2, item);
83
166
  });
84
167
  return __spreadProps(__spreadValues({}, acc), { [key]: container });
85
168
  }
86
- if (node instanceof import_loro_crdt.LoroText) {
169
+ if (node instanceof import_loro_crdt3.LoroMovableList) {
170
+ this._warn(oneLine`
171
+ Warning: You are using \`loro.moveableList\` to declare top-level storage value \`${key}\`.
172
+ Adding top-level values this way has been deprecated, to be removed in v2.
173
+ Please follow the v2 migration guide to declare top-level types correctly:
174
+ https://pluv.io/docs/migration-guides/v2
175
+ `);
176
+ const container = this.value.getMovableList(key);
177
+ node.toArray().forEach((item, i) => {
178
+ if ((0, import_loro_crdt3.isContainer)(item)) container.insertContainer(i, item);
179
+ else container.insert(i, item);
180
+ });
181
+ return __spreadProps(__spreadValues({}, acc), { [key]: container });
182
+ }
183
+ if (node instanceof import_loro_crdt3.LoroText) {
184
+ this._warn(oneLine`
185
+ Warning: You are using \`loro.text\` to declare top-level storage value \`${key}\`.
186
+ Adding top-level values this way has been deprecated, to be removed in v2.
187
+ Please follow the v2 migration guide to declare top-level types correctly:
188
+ https://pluv.io/docs/migration-guides/v2
189
+ `);
87
190
  const container = this.value.getText(key);
88
191
  container.insert(0, node.toString());
89
192
  return __spreadProps(__spreadValues({}, acc), { [key]: container });
90
193
  }
194
+ if (node instanceof import_loro_crdt3.LoroTree) {
195
+ this._warn(oneLine`
196
+ Warning: You are using \`loro.tree\` to declare top-level storage value \`${key}\`.
197
+ Adding top-level values this way has been deprecated, to be removed in v2.
198
+ Please follow the v2 migration guide to declare top-level types correctly:
199
+ https://pluv.io/docs/migration-guides/v2
200
+ `);
201
+ const container = this.value.getTree(key);
202
+ return __spreadProps(__spreadValues({}, acc), { [key]: container });
203
+ }
91
204
  return acc;
92
205
  }, {});
93
206
  this.value.commit();
@@ -143,11 +256,11 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
143
256
  toJson(type) {
144
257
  if (typeof type === "string") {
145
258
  const container = this._storage[type];
146
- return container instanceof import_loro_crdt.LoroText ? container.toString() : container instanceof import_loro_crdt.LoroCounter ? container.value : container.toJSON();
259
+ return container instanceof import_loro_crdt3.LoroText ? container.toString() : container instanceof import_loro_crdt3.LoroCounter ? container.value : container.toJSON();
147
260
  }
148
261
  return Object.entries(this._storage).reduce(
149
262
  (acc, [key, value]) => __spreadProps(__spreadValues({}, acc), {
150
- [key]: value instanceof import_loro_crdt.LoroText ? value.toString() : value.toJSON()
263
+ [key]: value instanceof import_loro_crdt3.LoroText ? value.toString() : value.toJSON()
151
264
  }),
152
265
  {}
153
266
  );
@@ -156,16 +269,22 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
156
269
  const serialized = this.value.toJSON();
157
270
  return !serialized || !Object.keys(serialized).length;
158
271
  }
159
- rebuildStorage() {
272
+ rebuildStorage(reference) {
160
273
  const isBuilt = !!Object.keys(this._storage).length;
161
274
  if (isBuilt) {
162
275
  console.warn("Attempted to rebuild storage multiple times");
163
276
  return this;
164
277
  }
165
- const keys = Object.keys(this.value.toJSON());
166
- this._storage = keys.reduce((acc, key) => {
167
- const container = this.value.getByPath(key);
168
- return (0, import_loro_crdt.isContainer)(container) ? __spreadProps(__spreadValues({}, acc), { [key]: container }) : acc;
278
+ this._storage = Object.entries(reference).reduce((acc, [key, node]) => {
279
+ if (node instanceof import_loro_crdt3.LoroCounter) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getCounter(key) });
280
+ if (node instanceof import_loro_crdt3.LoroList) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getList(key) });
281
+ if (node instanceof import_loro_crdt3.LoroMap) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getMap(key) });
282
+ if (node instanceof import_loro_crdt3.LoroMovableList) {
283
+ return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getMovableList(key) });
284
+ }
285
+ if (node instanceof import_loro_crdt3.LoroText) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getText(key) });
286
+ if (node instanceof import_loro_crdt3.LoroTree) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getTree(key) });
287
+ return acc;
169
288
  }, {});
170
289
  return this.track();
171
290
  }
@@ -203,7 +322,7 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
203
322
  this._undoManager.free();
204
323
  this._undoManager = null;
205
324
  }
206
- this._undoManager = new import_loro_crdt.UndoManager(this.value, {
325
+ this._undoManager = new import_loro_crdt3.UndoManager(this.value, {
207
326
  maxUndoSteps: MAX_UNDO_STEPS,
208
327
  mergeInterval: MERGE_INTERVAL_MS
209
328
  });
@@ -223,13 +342,19 @@ var CrdtLoroDoc = class extends import_crdt.AbstractCrdtDoc {
223
342
  (_a = this._undoManager) == null ? void 0 : _a.undo();
224
343
  return this;
225
344
  }
345
+ _warn(...data) {
346
+ var _a;
347
+ if (typeof process === "undefined") return;
348
+ if (((_a = process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
349
+ console.log(...data);
350
+ }
226
351
  };
227
352
 
228
353
  // src/doc/CrdtLoroDocFactory.ts
229
- var import_crdt2 = require("@pluv/crdt");
230
- var CrdtLoroDocFactory = class _CrdtLoroDocFactory extends import_crdt2.AbstractCrdtDocFactory {
354
+ var import_crdt = require("@pluv/crdt");
355
+ var CrdtLoroDocFactory = class _CrdtLoroDocFactory extends import_crdt.AbstractCrdtDocFactory {
231
356
  constructor(initialStorage = () => ({})) {
232
- super();
357
+ super(initialStorage);
233
358
  this._initialStorage = initialStorage;
234
359
  }
235
360
  getEmpty() {
@@ -239,8 +364,7 @@ var CrdtLoroDocFactory = class _CrdtLoroDocFactory extends import_crdt2.Abstract
239
364
  return new _CrdtLoroDocFactory(initialStorage != null ? initialStorage : this._initialStorage);
240
365
  }
241
366
  getInitialized(initialStorage) {
242
- var _a;
243
- return new CrdtLoroDoc((_a = initialStorage == null ? void 0 : initialStorage()) != null ? _a : this._initialStorage());
367
+ return new CrdtLoroDoc(initialStorage != null ? initialStorage : this._initialStorage);
244
368
  }
245
369
  };
246
370
 
@@ -250,61 +374,61 @@ var doc = (value = () => ({})) => {
250
374
  };
251
375
 
252
376
  // src/list.ts
253
- var import_loro_crdt2 = require("loro-crdt");
377
+ var import_loro_crdt4 = require("loro-crdt");
254
378
  var list = (value = []) => {
255
- const container = new import_loro_crdt2.LoroList();
379
+ const container = new import_loro_crdt4.LoroList();
256
380
  value.forEach((item, i) => {
257
- if ((0, import_loro_crdt2.isContainer)(item)) container.insertContainer(i, item);
381
+ if ((0, import_loro_crdt4.isContainer)(item)) container.insertContainer(i, item);
258
382
  else container.insert(i, item);
259
383
  });
260
384
  return container;
261
385
  };
262
386
 
263
387
  // src/map.ts
264
- var import_loro_crdt3 = require("loro-crdt");
388
+ var import_loro_crdt5 = require("loro-crdt");
265
389
  var map = (value = []) => {
266
- const container = new import_loro_crdt3.LoroMap();
390
+ const container = new import_loro_crdt5.LoroMap();
267
391
  value.forEach(([key, item]) => {
268
- if ((0, import_loro_crdt3.isContainer)(item)) container.setContainer(key, item);
392
+ if ((0, import_loro_crdt5.isContainer)(item)) container.setContainer(key, item);
269
393
  else container.set(key, item);
270
394
  });
271
395
  return container;
272
396
  };
273
397
 
274
398
  // src/movableList.ts
275
- var import_loro_crdt4 = require("loro-crdt");
399
+ var import_loro_crdt6 = require("loro-crdt");
276
400
  var movableList = (value = []) => {
277
- const container = new import_loro_crdt4.LoroMovableList();
401
+ const container = new import_loro_crdt6.LoroMovableList();
278
402
  value.forEach((item, i) => {
279
- if ((0, import_loro_crdt4.isContainer)(item)) container.insertContainer(i, item);
403
+ if ((0, import_loro_crdt6.isContainer)(item)) container.insertContainer(i, item);
280
404
  else container.insert(i, item);
281
405
  });
282
406
  return container;
283
407
  };
284
408
 
285
409
  // src/object.ts
286
- var import_loro_crdt5 = require("loro-crdt");
410
+ var import_loro_crdt7 = require("loro-crdt");
287
411
  var object = (value) => {
288
- const container = new import_loro_crdt5.LoroMap();
412
+ const container = new import_loro_crdt7.LoroMap();
289
413
  Object.entries(value).forEach(([key, item]) => {
290
- if ((0, import_loro_crdt5.isContainer)(item)) container.setContainer(key, item);
414
+ if ((0, import_loro_crdt7.isContainer)(item)) container.setContainer(key, item);
291
415
  else container.set(key, item);
292
416
  });
293
417
  return container;
294
418
  };
295
419
 
296
420
  // src/text.ts
297
- var import_loro_crdt6 = require("loro-crdt");
421
+ var import_loro_crdt8 = require("loro-crdt");
298
422
  var text = (value = "") => {
299
- const container = new import_loro_crdt6.LoroText();
423
+ const container = new import_loro_crdt8.LoroText();
300
424
  container.insert(0, value);
301
425
  return container;
302
426
  };
303
427
 
304
428
  // src/tree.ts
305
- var import_loro_crdt7 = require("loro-crdt");
429
+ var import_loro_crdt9 = require("loro-crdt");
306
430
  var tree = () => {
307
- const container = new import_loro_crdt7.LoroTree();
431
+ const container = new import_loro_crdt9.LoroTree();
308
432
  return container;
309
433
  };
310
434
 
package/dist/index.mjs CHANGED
@@ -26,6 +26,7 @@ var __export = (target, all) => {
26
26
  var loro_exports = {};
27
27
  __export(loro_exports, {
28
28
  CrdtLoroDoc: () => CrdtLoroDoc,
29
+ counter: () => counter,
29
30
  doc: () => doc,
30
31
  kind: () => kind,
31
32
  list: () => list,
@@ -36,47 +37,161 @@ __export(loro_exports, {
36
37
  tree: () => tree
37
38
  });
38
39
 
40
+ // src/counter.ts
41
+ import { LoroCounter } from "loro-crdt";
42
+ var counter = () => {
43
+ const counter2 = new LoroCounter();
44
+ return counter2;
45
+ };
46
+
39
47
  // src/doc/CrdtLoroDoc.ts
40
- import { AbstractCrdtDoc } from "@pluv/crdt";
41
48
  import { fromUint8Array, toUint8Array } from "js-base64";
42
49
  import {
43
- LoroCounter,
50
+ LoroCounter as LoroCounter2,
44
51
  LoroDoc,
45
52
  LoroList,
46
53
  LoroMap,
54
+ LoroMovableList,
47
55
  LoroText,
56
+ LoroTree,
48
57
  UndoManager,
49
- isContainer
58
+ isContainer as isContainer2
50
59
  } from "loro-crdt";
60
+
61
+ // src/utils/oneLine.ts
62
+ var oneLine = (strings, ...values) => {
63
+ return strings.map((str) => str.replace(/\s+/g, " ").trim()).reduce((acc, str, i) => acc + str + (i < values.length ? values[i] : ""), "").replace(/\s+/g, " ").trim();
64
+ };
65
+
66
+ // src/doc/builder.ts
67
+ import { isContainer } from "loro-crdt";
68
+ var builder = (doc2) => {
69
+ return {
70
+ counter(name) {
71
+ return doc2.getCounter(name);
72
+ },
73
+ list(name, value = []) {
74
+ const list2 = doc2.getList(name);
75
+ value.forEach((item, i) => {
76
+ if (isContainer(item)) list2.insertContainer(i, item);
77
+ else list2.insert(i, item);
78
+ });
79
+ return list2;
80
+ },
81
+ map(name, value = {}) {
82
+ const container = doc2.getMap(name);
83
+ Object.entries(value).forEach(([key, item]) => {
84
+ if (isContainer(item)) container.setContainer(key, item);
85
+ else container.set(key, item);
86
+ });
87
+ return container;
88
+ },
89
+ moveableList(name, value = []) {
90
+ const list2 = doc2.getMovableList(name);
91
+ value.forEach((item, i) => {
92
+ if (isContainer(item)) list2.insertContainer(i, item);
93
+ else list2.insert(i, item);
94
+ });
95
+ return list2;
96
+ },
97
+ text(name, value = "") {
98
+ const text2 = doc2.getText(name);
99
+ text2.insert(0, value);
100
+ return text2;
101
+ },
102
+ tree(name) {
103
+ return doc2.getTree(name);
104
+ }
105
+ };
106
+ };
107
+
108
+ // src/doc/CrdtLoroDoc.ts
51
109
  var MAX_UNDO_STEPS = 100;
52
110
  var MERGE_INTERVAL_MS = 1e3;
53
- var CrdtLoroDoc = class extends AbstractCrdtDoc {
54
- constructor(value = {}) {
55
- super();
111
+ var CrdtLoroDoc = class {
112
+ constructor(params = () => ({})) {
56
113
  this.value = new LoroDoc();
57
114
  this._undoManager = null;
58
- this._storage = Object.entries(value).reduce((acc, [key, node]) => {
115
+ const storage = params(builder(this.value));
116
+ const keys = Object.keys(this.value.toJSON()).reduce(
117
+ (set, key) => set.add(key),
118
+ /* @__PURE__ */ new Set()
119
+ );
120
+ this._storage = Object.entries(storage).reduce((acc, [key, node]) => {
121
+ if (keys.has(key)) return __spreadProps(__spreadValues({}, acc), { [key]: node });
122
+ if (node instanceof LoroCounter2) {
123
+ this._warn(oneLine`
124
+ Warning: You are using \`loro.counter\` to declare top-level storage value \`${key}\`.
125
+ Adding top-level values this way has been deprecated, to be removed in v2.
126
+ Please follow the v2 migration guide to declare top-level types correctly:
127
+ https://pluv.io/docs/migration-guides/v2
128
+ `);
129
+ const container = this.value.getCounter(key);
130
+ return __spreadProps(__spreadValues({}, acc), { [key]: container });
131
+ }
59
132
  if (node instanceof LoroList) {
133
+ this._warn(oneLine`
134
+ Warning: You are using \`loro.list\` to declare top-level storage value \`${key}\`.
135
+ Adding top-level values this way has been deprecated, to be removed in v2.
136
+ Please follow the v2 migration guide to declare top-level types correctly:
137
+ https://pluv.io/docs/migration-guides/v2
138
+ `);
60
139
  const container = this.value.getList(key);
61
140
  node.toArray().forEach((item, i) => {
62
- if (isContainer(item)) container.insertContainer(i, item);
141
+ if (isContainer2(item)) container.insertContainer(i, item);
63
142
  else container.insert(i, item);
64
143
  });
65
144
  return __spreadProps(__spreadValues({}, acc), { [key]: container });
66
145
  }
67
146
  if (node instanceof LoroMap) {
147
+ this._warn(oneLine`
148
+ Warning: You are using \`loro.map\` to declare top-level storage value \`${key}\`.
149
+ Adding top-level values this way has been deprecated, to be removed in v2.
150
+ Please follow the v2 migration guide to declare top-level types correctly:
151
+ https://pluv.io/docs/migration-guides/v2
152
+ `);
68
153
  const container = this.value.getMap(key);
69
154
  container.entries().forEach(([key2, item]) => {
70
- if (isContainer(item)) container.setContainer(key2, item);
155
+ if (isContainer2(item)) container.setContainer(key2, item);
71
156
  else container.set(key2, item);
72
157
  });
73
158
  return __spreadProps(__spreadValues({}, acc), { [key]: container });
74
159
  }
160
+ if (node instanceof LoroMovableList) {
161
+ this._warn(oneLine`
162
+ Warning: You are using \`loro.moveableList\` to declare top-level storage value \`${key}\`.
163
+ Adding top-level values this way has been deprecated, to be removed in v2.
164
+ Please follow the v2 migration guide to declare top-level types correctly:
165
+ https://pluv.io/docs/migration-guides/v2
166
+ `);
167
+ const container = this.value.getMovableList(key);
168
+ node.toArray().forEach((item, i) => {
169
+ if (isContainer2(item)) container.insertContainer(i, item);
170
+ else container.insert(i, item);
171
+ });
172
+ return __spreadProps(__spreadValues({}, acc), { [key]: container });
173
+ }
75
174
  if (node instanceof LoroText) {
175
+ this._warn(oneLine`
176
+ Warning: You are using \`loro.text\` to declare top-level storage value \`${key}\`.
177
+ Adding top-level values this way has been deprecated, to be removed in v2.
178
+ Please follow the v2 migration guide to declare top-level types correctly:
179
+ https://pluv.io/docs/migration-guides/v2
180
+ `);
76
181
  const container = this.value.getText(key);
77
182
  container.insert(0, node.toString());
78
183
  return __spreadProps(__spreadValues({}, acc), { [key]: container });
79
184
  }
185
+ if (node instanceof LoroTree) {
186
+ this._warn(oneLine`
187
+ Warning: You are using \`loro.tree\` to declare top-level storage value \`${key}\`.
188
+ Adding top-level values this way has been deprecated, to be removed in v2.
189
+ Please follow the v2 migration guide to declare top-level types correctly:
190
+ https://pluv.io/docs/migration-guides/v2
191
+ `);
192
+ const container = this.value.getTree(key);
193
+ return __spreadProps(__spreadValues({}, acc), { [key]: container });
194
+ }
80
195
  return acc;
81
196
  }, {});
82
197
  this.value.commit();
@@ -132,7 +247,7 @@ var CrdtLoroDoc = class extends AbstractCrdtDoc {
132
247
  toJson(type) {
133
248
  if (typeof type === "string") {
134
249
  const container = this._storage[type];
135
- return container instanceof LoroText ? container.toString() : container instanceof LoroCounter ? container.value : container.toJSON();
250
+ return container instanceof LoroText ? container.toString() : container instanceof LoroCounter2 ? container.value : container.toJSON();
136
251
  }
137
252
  return Object.entries(this._storage).reduce(
138
253
  (acc, [key, value]) => __spreadProps(__spreadValues({}, acc), {
@@ -145,16 +260,22 @@ var CrdtLoroDoc = class extends AbstractCrdtDoc {
145
260
  const serialized = this.value.toJSON();
146
261
  return !serialized || !Object.keys(serialized).length;
147
262
  }
148
- rebuildStorage() {
263
+ rebuildStorage(reference) {
149
264
  const isBuilt = !!Object.keys(this._storage).length;
150
265
  if (isBuilt) {
151
266
  console.warn("Attempted to rebuild storage multiple times");
152
267
  return this;
153
268
  }
154
- const keys = Object.keys(this.value.toJSON());
155
- this._storage = keys.reduce((acc, key) => {
156
- const container = this.value.getByPath(key);
157
- return isContainer(container) ? __spreadProps(__spreadValues({}, acc), { [key]: container }) : acc;
269
+ this._storage = Object.entries(reference).reduce((acc, [key, node]) => {
270
+ if (node instanceof LoroCounter2) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getCounter(key) });
271
+ if (node instanceof LoroList) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getList(key) });
272
+ if (node instanceof LoroMap) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getMap(key) });
273
+ if (node instanceof LoroMovableList) {
274
+ return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getMovableList(key) });
275
+ }
276
+ if (node instanceof LoroText) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getText(key) });
277
+ if (node instanceof LoroTree) return __spreadProps(__spreadValues({}, acc), { [key]: this.value.getTree(key) });
278
+ return acc;
158
279
  }, {});
159
280
  return this.track();
160
281
  }
@@ -212,13 +333,19 @@ var CrdtLoroDoc = class extends AbstractCrdtDoc {
212
333
  (_a = this._undoManager) == null ? void 0 : _a.undo();
213
334
  return this;
214
335
  }
336
+ _warn(...data) {
337
+ var _a;
338
+ if (typeof process === "undefined") return;
339
+ if (((_a = process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
340
+ console.log(...data);
341
+ }
215
342
  };
216
343
 
217
344
  // src/doc/CrdtLoroDocFactory.ts
218
345
  import { AbstractCrdtDocFactory } from "@pluv/crdt";
219
346
  var CrdtLoroDocFactory = class _CrdtLoroDocFactory extends AbstractCrdtDocFactory {
220
347
  constructor(initialStorage = () => ({})) {
221
- super();
348
+ super(initialStorage);
222
349
  this._initialStorage = initialStorage;
223
350
  }
224
351
  getEmpty() {
@@ -228,8 +355,7 @@ var CrdtLoroDocFactory = class _CrdtLoroDocFactory extends AbstractCrdtDocFactor
228
355
  return new _CrdtLoroDocFactory(initialStorage != null ? initialStorage : this._initialStorage);
229
356
  }
230
357
  getInitialized(initialStorage) {
231
- var _a;
232
- return new CrdtLoroDoc((_a = initialStorage == null ? void 0 : initialStorage()) != null ? _a : this._initialStorage());
358
+ return new CrdtLoroDoc(initialStorage != null ? initialStorage : this._initialStorage);
233
359
  }
234
360
  };
235
361
 
@@ -239,44 +365,44 @@ var doc = (value = () => ({})) => {
239
365
  };
240
366
 
241
367
  // src/list.ts
242
- import { LoroList as LoroList2, isContainer as isContainer2 } from "loro-crdt";
368
+ import { LoroList as LoroList2, isContainer as isContainer3 } from "loro-crdt";
243
369
  var list = (value = []) => {
244
370
  const container = new LoroList2();
245
371
  value.forEach((item, i) => {
246
- if (isContainer2(item)) container.insertContainer(i, item);
372
+ if (isContainer3(item)) container.insertContainer(i, item);
247
373
  else container.insert(i, item);
248
374
  });
249
375
  return container;
250
376
  };
251
377
 
252
378
  // src/map.ts
253
- import { LoroMap as LoroMap2, isContainer as isContainer3 } from "loro-crdt";
379
+ import { LoroMap as LoroMap2, isContainer as isContainer4 } from "loro-crdt";
254
380
  var map = (value = []) => {
255
381
  const container = new LoroMap2();
256
382
  value.forEach(([key, item]) => {
257
- if (isContainer3(item)) container.setContainer(key, item);
383
+ if (isContainer4(item)) container.setContainer(key, item);
258
384
  else container.set(key, item);
259
385
  });
260
386
  return container;
261
387
  };
262
388
 
263
389
  // src/movableList.ts
264
- import { isContainer as isContainer4, LoroMovableList } from "loro-crdt";
390
+ import { isContainer as isContainer5, LoroMovableList as LoroMovableList2 } from "loro-crdt";
265
391
  var movableList = (value = []) => {
266
- const container = new LoroMovableList();
392
+ const container = new LoroMovableList2();
267
393
  value.forEach((item, i) => {
268
- if (isContainer4(item)) container.insertContainer(i, item);
394
+ if (isContainer5(item)) container.insertContainer(i, item);
269
395
  else container.insert(i, item);
270
396
  });
271
397
  return container;
272
398
  };
273
399
 
274
400
  // src/object.ts
275
- import { LoroMap as LoroMap3, isContainer as isContainer5 } from "loro-crdt";
401
+ import { LoroMap as LoroMap3, isContainer as isContainer6 } from "loro-crdt";
276
402
  var object = (value) => {
277
403
  const container = new LoroMap3();
278
404
  Object.entries(value).forEach(([key, item]) => {
279
- if (isContainer5(item)) container.setContainer(key, item);
405
+ if (isContainer6(item)) container.setContainer(key, item);
280
406
  else container.set(key, item);
281
407
  });
282
408
  return container;
@@ -291,9 +417,9 @@ var text = (value = "") => {
291
417
  };
292
418
 
293
419
  // src/tree.ts
294
- import { LoroTree } from "loro-crdt";
420
+ import { LoroTree as LoroTree2 } from "loro-crdt";
295
421
  var tree = () => {
296
- const container = new LoroTree();
422
+ const container = new LoroTree2();
297
423
  return container;
298
424
  };
299
425
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/crdt-loro",
3
- "version": "1.0.1",
3
+ "version": "2.0.0",
4
4
  "description": "loro for @pluv/io",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,19 +18,20 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "js-base64": "^3.7.7",
21
- "@pluv/crdt": "^1.0.1",
22
- "@pluv/types": "^1.0.1"
21
+ "@pluv/crdt": "^2.0.0",
22
+ "@pluv/types": "^2.0.0"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "loro-crdt": "^1.0.0"
26
26
  },
27
27
  "devDependencies": {
28
+ "@types/node": "^22.15.3",
28
29
  "eslint": "^9.25.1",
29
30
  "loro-crdt": "^1.5.4",
30
31
  "tsup": "^8.4.0",
31
32
  "typescript": "^5.8.3",
32
- "@pluv/tsconfig": "^1.0.1",
33
- "eslint-config-pluv": "^1.0.1"
33
+ "@pluv/tsconfig": "^2.0.0",
34
+ "eslint-config-pluv": "^2.0.0"
34
35
  },
35
36
  "scripts": {
36
37
  "build": "tsup src/index.ts --format esm,cjs --dts",
package/src/counter.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { LoroCounter } from "loro-crdt";
2
+ import { LoroType } from "./types";
3
+
4
+ export const counter = (): LoroCounter => {
5
+ const counter = new LoroCounter();
6
+
7
+ return counter as unknown as LoroType<LoroCounter, number>;
8
+ };
@@ -4,7 +4,7 @@ import type {
4
4
  DocSubscribeCallbackParams,
5
5
  InferCrdtJson,
6
6
  } from "@pluv/crdt";
7
- import { AbstractCrdtDoc } from "@pluv/crdt";
7
+ import type { CrdtDocLike } from "@pluv/types";
8
8
  import { fromUint8Array, toUint8Array } from "js-base64";
9
9
  import type { Container } from "loro-crdt";
10
10
  import {
@@ -13,28 +13,69 @@ import {
13
13
  LoroEventBatch,
14
14
  LoroList,
15
15
  LoroMap,
16
+ LoroMovableList,
16
17
  LoroText,
18
+ LoroTree,
17
19
  UndoManager,
18
20
  isContainer,
19
21
  } from "loro-crdt";
20
22
  import type { LoroType } from "../types";
23
+ import { oneLine } from "../utils";
24
+ import type { LoroBuilder } from "./builder";
25
+ import { builder } from "./builder";
21
26
 
22
27
  const MAX_UNDO_STEPS = 100;
23
28
  const MERGE_INTERVAL_MS = 1_000;
24
29
 
25
- export class CrdtLoroDoc<
26
- TStorage extends Record<string, LoroType<any, any>>,
27
- > extends AbstractCrdtDoc<TStorage> {
30
+ export type CrdtLoroDocParams<TStorage extends Record<string, LoroType<any, any>>> = (
31
+ builder: LoroBuilder,
32
+ ) => TStorage;
33
+
34
+ export class CrdtLoroDoc<TStorage extends Record<string, LoroType<any, any>>>
35
+ implements CrdtDocLike<TStorage>
36
+ {
28
37
  public value: LoroDoc = new LoroDoc();
29
38
 
30
39
  private _storage: TStorage;
31
40
  private _undoManager: UndoManager | null = null;
32
41
 
33
- constructor(value: TStorage = {} as TStorage) {
34
- super();
42
+ constructor(params: CrdtLoroDocParams<TStorage> = () => ({}) as TStorage) {
43
+ const storage = params(builder(this.value));
44
+
45
+ const keys = Object.keys(this.value.toJSON()).reduce(
46
+ (set, key) => set.add(key),
47
+ new Set<string>(),
48
+ );
49
+
50
+ this._storage = Object.entries(storage).reduce((acc, [key, node]) => {
51
+ /**
52
+ * @description These are all container types that we declared directly on the root
53
+ * document. So we're going to store these on the storage type directly.
54
+ * @date May 8, 2025
55
+ */
56
+ if (keys.has(key)) return { ...acc, [key]: node };
57
+
58
+ if (node instanceof LoroCounter) {
59
+ this._warn(oneLine`
60
+ Warning: You are using \`loro.counter\` to declare top-level storage value \`${key}\`.
61
+ Adding top-level values this way has been deprecated, to be removed in v2.
62
+ Please follow the v2 migration guide to declare top-level types correctly:
63
+ https://pluv.io/docs/migration-guides/v2
64
+ `);
65
+
66
+ const container = this.value.getCounter(key);
67
+
68
+ return { ...acc, [key]: container };
69
+ }
35
70
 
36
- this._storage = Object.entries(value).reduce((acc, [key, node]) => {
37
71
  if (node instanceof LoroList) {
72
+ this._warn(oneLine`
73
+ Warning: You are using \`loro.list\` to declare top-level storage value \`${key}\`.
74
+ Adding top-level values this way has been deprecated, to be removed in v2.
75
+ Please follow the v2 migration guide to declare top-level types correctly:
76
+ https://pluv.io/docs/migration-guides/v2
77
+ `);
78
+
38
79
  const container = this.value.getList(key);
39
80
 
40
81
  node.toArray().forEach((item, i) => {
@@ -46,6 +87,13 @@ export class CrdtLoroDoc<
46
87
  }
47
88
 
48
89
  if (node instanceof LoroMap) {
90
+ this._warn(oneLine`
91
+ Warning: You are using \`loro.map\` to declare top-level storage value \`${key}\`.
92
+ Adding top-level values this way has been deprecated, to be removed in v2.
93
+ Please follow the v2 migration guide to declare top-level types correctly:
94
+ https://pluv.io/docs/migration-guides/v2
95
+ `);
96
+
49
97
  const container = this.value.getMap(key);
50
98
 
51
99
  container.entries().forEach(([key, item]) => {
@@ -56,7 +104,32 @@ export class CrdtLoroDoc<
56
104
  return { ...acc, [key]: container };
57
105
  }
58
106
 
107
+ if (node instanceof LoroMovableList) {
108
+ this._warn(oneLine`
109
+ Warning: You are using \`loro.moveableList\` to declare top-level storage value \`${key}\`.
110
+ Adding top-level values this way has been deprecated, to be removed in v2.
111
+ Please follow the v2 migration guide to declare top-level types correctly:
112
+ https://pluv.io/docs/migration-guides/v2
113
+ `);
114
+
115
+ const container = this.value.getMovableList(key);
116
+
117
+ node.toArray().forEach((item, i) => {
118
+ if (isContainer(item)) container.insertContainer(i, item);
119
+ else container.insert(i, item);
120
+ });
121
+
122
+ return { ...acc, [key]: container };
123
+ }
124
+
59
125
  if (node instanceof LoroText) {
126
+ this._warn(oneLine`
127
+ Warning: You are using \`loro.text\` to declare top-level storage value \`${key}\`.
128
+ Adding top-level values this way has been deprecated, to be removed in v2.
129
+ Please follow the v2 migration guide to declare top-level types correctly:
130
+ https://pluv.io/docs/migration-guides/v2
131
+ `);
132
+
60
133
  const container = this.value.getText(key);
61
134
 
62
135
  container.insert(0, node.toString());
@@ -64,6 +137,19 @@ export class CrdtLoroDoc<
64
137
  return { ...acc, [key]: container };
65
138
  }
66
139
 
140
+ if (node instanceof LoroTree) {
141
+ this._warn(oneLine`
142
+ Warning: You are using \`loro.tree\` to declare top-level storage value \`${key}\`.
143
+ Adding top-level values this way has been deprecated, to be removed in v2.
144
+ Please follow the v2 migration guide to declare top-level types correctly:
145
+ https://pluv.io/docs/migration-guides/v2
146
+ `);
147
+
148
+ const container = this.value.getTree(key);
149
+
150
+ return { ...acc, [key]: container };
151
+ }
152
+
67
153
  return acc;
68
154
  }, {} as TStorage);
69
155
 
@@ -171,7 +257,7 @@ export class CrdtLoroDoc<
171
257
  return !serialized || !Object.keys(serialized).length;
172
258
  }
173
259
 
174
- public rebuildStorage(): this {
260
+ public rebuildStorage(reference: TStorage): this {
175
261
  const isBuilt = !!Object.keys(this._storage).length;
176
262
 
177
263
  if (isBuilt) {
@@ -179,12 +265,17 @@ export class CrdtLoroDoc<
179
265
  return this;
180
266
  }
181
267
 
182
- const keys = Object.keys(this.value.toJSON());
183
-
184
- this._storage = keys.reduce((acc, key) => {
185
- const container = this.value.getByPath(key);
268
+ this._storage = Object.entries(reference).reduce((acc, [key, node]) => {
269
+ if (node instanceof LoroCounter) return { ...acc, [key]: this.value.getCounter(key) };
270
+ if (node instanceof LoroList) return { ...acc, [key]: this.value.getList(key) };
271
+ if (node instanceof LoroMap) return { ...acc, [key]: this.value.getMap(key) };
272
+ if (node instanceof LoroMovableList) {
273
+ return { ...acc, [key]: this.value.getMovableList(key) };
274
+ }
275
+ if (node instanceof LoroText) return { ...acc, [key]: this.value.getText(key) };
276
+ if (node instanceof LoroTree) return { ...acc, [key]: this.value.getTree(key) };
186
277
 
187
- return isContainer(container) ? { ...acc, [key]: container } : acc;
278
+ return acc;
188
279
  }, {} as TStorage);
189
280
 
190
281
  return this.track();
@@ -257,4 +348,10 @@ export class CrdtLoroDoc<
257
348
 
258
349
  return this;
259
350
  }
351
+
352
+ private _warn(...data: any[]) {
353
+ if (typeof process === "undefined") return;
354
+ if (process.env?.NODE_ENV === "production") return;
355
+ console.log(...data);
356
+ }
260
357
  }
@@ -1,14 +1,15 @@
1
1
  import { AbstractCrdtDocFactory } from "@pluv/crdt";
2
2
  import type { LoroType } from "../types";
3
3
  import { CrdtLoroDoc } from "./CrdtLoroDoc";
4
+ import type { LoroBuilder } from "./builder";
4
5
 
5
6
  export class CrdtLoroDocFactory<
6
7
  TStorage extends Record<string, LoroType<any, any>>,
7
8
  > extends AbstractCrdtDocFactory<TStorage> {
8
- private _initialStorage: () => TStorage;
9
+ public readonly _initialStorage: (builder: LoroBuilder) => TStorage;
9
10
 
10
- constructor(initialStorage: () => TStorage = () => ({}) as TStorage) {
11
- super();
11
+ constructor(initialStorage: (builder: LoroBuilder) => TStorage = () => ({}) as TStorage) {
12
+ super(initialStorage);
12
13
 
13
14
  this._initialStorage = initialStorage;
14
15
  }
@@ -17,11 +18,15 @@ export class CrdtLoroDocFactory<
17
18
  return new CrdtLoroDoc<TStorage>();
18
19
  }
19
20
 
20
- public getFactory(initialStorage?: (() => TStorage) | undefined): CrdtLoroDocFactory<TStorage> {
21
+ public getFactory(
22
+ initialStorage?: ((builder: LoroBuilder) => TStorage) | undefined,
23
+ ): CrdtLoroDocFactory<TStorage> {
21
24
  return new CrdtLoroDocFactory<TStorage>(initialStorage ?? this._initialStorage);
22
25
  }
23
26
 
24
- public getInitialized(initialStorage?: () => TStorage): CrdtLoroDoc<TStorage> {
25
- return new CrdtLoroDoc<TStorage>(initialStorage?.() ?? this._initialStorage());
27
+ public getInitialized(
28
+ initialStorage?: (builder: LoroBuilder) => TStorage,
29
+ ): CrdtLoroDoc<TStorage> {
30
+ return new CrdtLoroDoc<TStorage>(initialStorage ?? this._initialStorage);
26
31
  }
27
32
  }
@@ -0,0 +1,70 @@
1
+ import type {
2
+ Container,
3
+ LoroCounter,
4
+ LoroDoc,
5
+ LoroList,
6
+ LoroMap,
7
+ LoroMovableList,
8
+ LoroText,
9
+ } from "loro-crdt";
10
+ import { isContainer } from "loro-crdt";
11
+ import type { LoroType } from "../types";
12
+
13
+ export type LoroBuilder = ReturnType<typeof builder>;
14
+
15
+ export const builder = (doc: LoroDoc) => {
16
+ return {
17
+ counter(name: string): LoroCounter {
18
+ return doc.getCounter(name);
19
+ },
20
+ list<T extends unknown>(
21
+ name: string,
22
+ value: T[] | readonly T[] = [],
23
+ ): LoroType<LoroList<T>, T[]> {
24
+ const list = doc.getList(name);
25
+
26
+ value.forEach((item, i) => {
27
+ if (isContainer(item)) list.insertContainer(i, item);
28
+ else list.insert(i, item as Exclude<T, Container>);
29
+ });
30
+
31
+ return list as unknown as LoroType<LoroList<T>, T[]>;
32
+ },
33
+ map<T extends Record<string, unknown>>(
34
+ name: string,
35
+ value: T = {} as T,
36
+ ): LoroType<LoroMap<T>, T> {
37
+ const container = doc.getMap(name);
38
+
39
+ Object.entries(value).forEach(([key, item]) => {
40
+ if (isContainer(item)) container.setContainer(key, item);
41
+ else container.set(key, item as Exclude<T, Container>);
42
+ });
43
+
44
+ return container as unknown as LoroType<LoroMap<T>, T>;
45
+ },
46
+ moveableList<T extends unknown>(
47
+ name: string,
48
+ value: T[] | readonly T[] = [],
49
+ ): LoroType<LoroMovableList<T>, T[]> {
50
+ const list = doc.getMovableList(name);
51
+
52
+ value.forEach((item, i) => {
53
+ if (isContainer(item)) list.insertContainer(i, item);
54
+ else list.insert(i, item as Exclude<T, Container>);
55
+ });
56
+
57
+ return list as unknown as LoroType<LoroMovableList<T>, T[]>;
58
+ },
59
+ text(name: string, value: string = ""): LoroType<LoroText, string> {
60
+ const text = doc.getText(name);
61
+
62
+ text.insert(0, value);
63
+
64
+ return text as unknown as LoroType<LoroText, string>;
65
+ },
66
+ tree(name: string) {
67
+ return doc.getTree(name);
68
+ },
69
+ };
70
+ };
package/src/doc/doc.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import type { LoroType } from "../types";
2
+ import type { LoroBuilder } from "./builder";
2
3
  import { CrdtLoroDocFactory } from "./CrdtLoroDocFactory";
3
4
 
4
5
  export const doc = <TStorage extends Record<string, LoroType<any, any>>>(
5
- value: () => TStorage = () => ({}) as TStorage,
6
+ value: (builder: LoroBuilder) => TStorage = () => ({}) as TStorage,
6
7
  ): CrdtLoroDocFactory<TStorage> => {
7
8
  return new CrdtLoroDocFactory<TStorage>(value);
8
9
  };
package/src/doc/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type { LoroBuilder } from "./builder";
1
2
  export { CrdtLoroDoc } from "./CrdtLoroDoc";
2
3
  export { CrdtLoroDocFactory } from "./CrdtLoroDocFactory";
3
4
  export { doc } from "./doc";
package/src/loro.ts CHANGED
@@ -1,4 +1,6 @@
1
+ export { counter } from "./counter";
1
2
  export { CrdtLoroDoc, doc } from "./doc";
3
+ export type { LoroBuilder } from "./doc";
2
4
  export { list } from "./list";
3
5
  export { map } from "./map";
4
6
  export { movableList } from "./movableList";
@@ -0,0 +1 @@
1
+ export { oneLine } from "./oneLine";
@@ -0,0 +1,7 @@
1
+ export const oneLine = (strings: TemplateStringsArray, ...values: unknown[]): string => {
2
+ return strings
3
+ .map((str) => str.replace(/\s+/g, " ").trim())
4
+ .reduce((acc, str, i) => acc + str + (i < values.length ? values[i] : ""), "")
5
+ .replace(/\s+/g, " ")
6
+ .trim();
7
+ };