@loro-extended/change 3.0.0 → 5.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.
- package/README.md +289 -150
- package/dist/index.d.ts +1012 -310
- package/dist/index.js +1334 -568
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/change.test.ts +51 -52
- package/src/functional-helpers.test.ts +316 -4
- package/src/functional-helpers.ts +96 -6
- package/src/grand-unified-api.test.ts +35 -29
- package/src/index.ts +27 -1
- package/src/json-patch.test.ts +46 -27
- package/src/loro.test.ts +449 -0
- package/src/loro.ts +273 -0
- package/src/overlay-recursion.test.ts +1 -1
- package/src/overlay.ts +62 -3
- package/src/path-evaluator.ts +1 -1
- package/src/path-selector.test.ts +94 -1
- package/src/shape.ts +107 -14
- package/src/typed-doc.ts +100 -98
- package/src/typed-refs/base.ts +126 -46
- package/src/typed-refs/counter-ref-internals.ts +62 -0
- package/src/typed-refs/{counter.test.ts → counter-ref.test.ts} +5 -4
- package/src/typed-refs/counter-ref.ts +45 -0
- package/src/typed-refs/{doc.ts → doc-ref-internals.ts} +33 -56
- package/src/typed-refs/doc-ref.ts +47 -0
- package/src/typed-refs/encapsulation.test.ts +226 -0
- package/src/typed-refs/list-ref-base-internals.ts +280 -0
- package/src/typed-refs/list-ref-base.ts +518 -0
- package/src/typed-refs/list-ref-internals.ts +21 -0
- package/src/typed-refs/list-ref-value-updates.test.ts +213 -0
- package/src/typed-refs/{list.ts → list-ref.ts} +10 -11
- package/src/typed-refs/movable-list-ref-internals.ts +38 -0
- package/src/typed-refs/movable-list-ref.ts +31 -0
- package/src/typed-refs/proxy-handlers.ts +13 -4
- package/src/typed-refs/record-ref-internals.ts +216 -0
- package/src/typed-refs/record-ref-value-updates.test.ts +214 -0
- package/src/typed-refs/{record.test.ts → record-ref.test.ts} +21 -16
- package/src/typed-refs/record-ref.ts +80 -0
- package/src/typed-refs/struct-ref-internals.ts +195 -0
- package/src/typed-refs/struct-ref.test.ts +202 -0
- package/src/typed-refs/struct-ref.ts +257 -0
- package/src/typed-refs/text-ref-internals.ts +100 -0
- package/src/typed-refs/text-ref.ts +72 -0
- package/src/typed-refs/tree-node-ref-internals.ts +111 -0
- package/src/typed-refs/tree-node-ref.test.ts +234 -0
- package/src/typed-refs/tree-node-ref.ts +200 -0
- package/src/typed-refs/tree-node.test.ts +384 -0
- package/src/typed-refs/tree-ref-internals.ts +110 -0
- package/src/typed-refs/tree-ref.ts +194 -0
- package/src/typed-refs/utils.ts +38 -17
- package/src/types.ts +36 -1
- package/src/utils/type-guards.ts +1 -0
- package/src/typed-refs/counter.ts +0 -64
- package/src/typed-refs/list-base.ts +0 -424
- package/src/typed-refs/movable-list.ts +0 -34
- package/src/typed-refs/record.ts +0 -220
- package/src/typed-refs/struct.ts +0 -206
- package/src/typed-refs/text.ts +0 -97
- package/src/typed-refs/tree.ts +0 -40
- /package/src/typed-refs/{list.test.ts → list-ref.test.ts} +0 -0
- /package/src/typed-refs/{movable-list.test.ts → movable-list-ref.test.ts} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
import { LoroDoc,
|
|
1
|
+
import { LoroDoc, Container, LoroTreeNode, TreeID, Subscription, LoroText, LoroList, LoroMovableList, LoroMap, LoroCounter, LoroTree, Value } from 'loro-crdt';
|
|
2
2
|
|
|
3
|
+
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
4
|
+
/**
|
|
5
|
+
* Expands a type to a reasonable depth for IDE display.
|
|
6
|
+
* Stops at depth 4 to avoid infinite recursion with self-referential types
|
|
7
|
+
* like tree nodes that have `children: TreeNodeJSON[]`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // Without expansion, hover shows: TreeNodeJSON<StructContainerShape<...>>
|
|
12
|
+
* // With expansion, hover shows the actual structure:
|
|
13
|
+
* // { id: string; parent: string | null; data: { name: string; ... }; children: ... }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
type ExpandDeep<T, Depth extends number = 4> = Depth extends 0 ? T : T extends (...args: any[]) => any ? T : T extends object ? T extends infer O ? {
|
|
17
|
+
[K in keyof O]: ExpandDeep<O[K], Prev[Depth]>;
|
|
18
|
+
} : never : T;
|
|
3
19
|
/**
|
|
4
20
|
* Infers the plain (JSON-serializable) type from any Shape.
|
|
21
|
+
* The result is fully expanded for better IDE hover display.
|
|
5
22
|
*
|
|
6
23
|
* This is the recommended way to extract types from shapes.
|
|
7
24
|
* Works with DocShape, ContainerShape, and ValueShape.
|
|
@@ -29,7 +46,13 @@ import { LoroDoc, LoroCounter, LoroList, LoroMovableList, Container, LoroMap, Va
|
|
|
29
46
|
* // Result: { name: string; cursor: { x: number; y: number } }
|
|
30
47
|
* ```
|
|
31
48
|
*/
|
|
32
|
-
type Infer<T> = T extends Shape<infer P, any, any> ? P : never;
|
|
49
|
+
type Infer<T> = T extends Shape<infer P, any, any> ? ExpandDeep<P> : never;
|
|
50
|
+
/**
|
|
51
|
+
* Infers the plain (JSON-serializable) type from any Shape without expansion.
|
|
52
|
+
* Use this if you prefer to see type alias names (like TreeNodeJSON) in hover displays,
|
|
53
|
+
* or if you need slightly faster type checking on very large schemas.
|
|
54
|
+
*/
|
|
55
|
+
type InferRaw<T> = T extends Shape<infer P, any, any> ? P : never;
|
|
33
56
|
/**
|
|
34
57
|
* Infers the mutable type from any Shape.
|
|
35
58
|
* This is the type used within change() callbacks for mutation.
|
|
@@ -48,71 +71,177 @@ type InferPlaceholderType<T> = T extends Shape<any, any, infer P> ? P : never;
|
|
|
48
71
|
*/
|
|
49
72
|
type Mutable<T extends DocShape<Record<string, ContainerShape>>> = InferMutableType<T>;
|
|
50
73
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
getDoc?: () => LoroDoc;
|
|
74
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: JSON Patch values can be any type */
|
|
75
|
+
|
|
76
|
+
type JsonPatchAddOperation = {
|
|
77
|
+
op: "add";
|
|
78
|
+
path: string | (string | number)[];
|
|
79
|
+
value: any;
|
|
58
80
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
81
|
+
type JsonPatchRemoveOperation = {
|
|
82
|
+
op: "remove";
|
|
83
|
+
path: string | (string | number)[];
|
|
84
|
+
};
|
|
85
|
+
type JsonPatchReplaceOperation = {
|
|
86
|
+
op: "replace";
|
|
87
|
+
path: string | (string | number)[];
|
|
88
|
+
value: any;
|
|
89
|
+
};
|
|
90
|
+
type JsonPatchMoveOperation = {
|
|
91
|
+
op: "move";
|
|
92
|
+
path: string | (string | number)[];
|
|
93
|
+
from: string | (string | number)[];
|
|
94
|
+
};
|
|
95
|
+
type JsonPatchCopyOperation = {
|
|
96
|
+
op: "copy";
|
|
97
|
+
path: string | (string | number)[];
|
|
98
|
+
from: string | (string | number)[];
|
|
99
|
+
};
|
|
100
|
+
type JsonPatchTestOperation = {
|
|
101
|
+
op: "test";
|
|
102
|
+
path: string | (string | number)[];
|
|
103
|
+
value: any;
|
|
104
|
+
};
|
|
105
|
+
type JsonPatchOperation = JsonPatchAddOperation | JsonPatchRemoveOperation | JsonPatchReplaceOperation | JsonPatchMoveOperation | JsonPatchCopyOperation | JsonPatchTestOperation;
|
|
106
|
+
type JsonPatch = JsonPatchOperation[];
|
|
107
|
+
|
|
108
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: fix later */
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The proxied TypedDoc type that provides direct schema access.
|
|
112
|
+
* Schema properties are accessed directly on the doc object.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const doc = createTypedDoc(schema);
|
|
117
|
+
*
|
|
118
|
+
* // Direct schema access
|
|
119
|
+
* doc.count.increment(5);
|
|
120
|
+
* doc.title.insert(0, "Hello");
|
|
121
|
+
*
|
|
122
|
+
* // Serialize to JSON (works on doc and all refs)
|
|
123
|
+
* const snapshot = doc.toJSON();
|
|
124
|
+
* const users = doc.users.toJSON();
|
|
125
|
+
*
|
|
126
|
+
* // Batched mutations via change()
|
|
127
|
+
* doc.change(draft => {
|
|
128
|
+
* draft.count.increment(10);
|
|
129
|
+
* draft.title.update("World");
|
|
130
|
+
* });
|
|
131
|
+
*
|
|
132
|
+
* // Access CRDT internals via loro()
|
|
133
|
+
* import { loro } from "@loro-extended/change";
|
|
134
|
+
* loro(doc).doc; // LoroDoc
|
|
135
|
+
* loro(doc).subscribe(callback);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
type TypedDoc<Shape extends DocShape> = Mutable<Shape> & {
|
|
74
139
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
140
|
+
* The primary method of mutating typed documents.
|
|
141
|
+
* Batches multiple mutations into a single transaction.
|
|
142
|
+
* All changes commit together at the end.
|
|
143
|
+
*
|
|
144
|
+
* Use this for:
|
|
145
|
+
* - Find-and-mutate operations (required due to JS limitations)
|
|
146
|
+
* - Performance (fewer commits)
|
|
147
|
+
* - Atomic undo (all changes = one undo step)
|
|
148
|
+
*
|
|
149
|
+
* Returns the doc for chaining.
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* doc.change(draft => {
|
|
154
|
+
* draft.count.increment(10);
|
|
155
|
+
* draft.title.update("World");
|
|
156
|
+
* });
|
|
157
|
+
* ```
|
|
77
158
|
*/
|
|
78
|
-
|
|
159
|
+
change(fn: (draft: Mutable<Shape>) => void): TypedDoc<Shape>;
|
|
79
160
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
161
|
+
* Returns the full plain JavaScript object representation of the document.
|
|
162
|
+
* This is an O(N) operation that serializes the entire document.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* const snapshot = doc.toJSON();
|
|
167
|
+
* console.log(snapshot.count); // number
|
|
168
|
+
* ```
|
|
83
169
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
170
|
+
toJSON(): Infer<Shape>;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Creates a new TypedDoc with the given schema.
|
|
174
|
+
* Returns a proxied document where schema properties are accessed directly.
|
|
175
|
+
*
|
|
176
|
+
* @param shape - The document schema (with optional .placeholder() values)
|
|
177
|
+
* @param existingDoc - Optional existing LoroDoc to wrap
|
|
178
|
+
* @returns A proxied TypedDoc with direct schema access
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```typescript
|
|
182
|
+
* const schema = Shape.doc({
|
|
183
|
+
* title: Shape.text(),
|
|
184
|
+
* count: Shape.counter(),
|
|
185
|
+
* });
|
|
186
|
+
*
|
|
187
|
+
* const doc = createTypedDoc(schema);
|
|
188
|
+
*
|
|
189
|
+
* // Direct mutations (auto-commit)
|
|
190
|
+
* doc.count.increment(5);
|
|
191
|
+
* doc.title.insert(0, "Hello");
|
|
192
|
+
*
|
|
193
|
+
* // Batched mutations via change()
|
|
194
|
+
* doc.change(draft => {
|
|
195
|
+
* draft.count.increment(10);
|
|
196
|
+
* draft.title.update("World");
|
|
197
|
+
* });
|
|
198
|
+
*
|
|
199
|
+
* // Get plain JSON
|
|
200
|
+
* const snapshot = doc.toJSON();
|
|
201
|
+
*
|
|
202
|
+
* // Access CRDT internals via loro()
|
|
203
|
+
* import { loro } from "@loro-extended/change";
|
|
204
|
+
* loro(doc).doc; // LoroDoc
|
|
205
|
+
* loro(doc).subscribe(callback);
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDoc?: LoroDoc): TypedDoc<Shape>;
|
|
87
209
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Internal implementation for ListRefBase.
|
|
212
|
+
* Contains all logic, state, and implementation details for list operations.
|
|
213
|
+
*/
|
|
214
|
+
declare class ListRefBaseInternals<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"], MutableItem = NestedShape["_mutable"]> extends BaseRefInternals<any> {
|
|
215
|
+
private itemCache;
|
|
216
|
+
/** Get typed ref params for creating child refs at an index */
|
|
217
|
+
getTypedRefParams(index: number, shape: ContainerShape): TypedRefParams<ContainerShape>;
|
|
218
|
+
/** Get item for predicate functions (returns plain value) */
|
|
219
|
+
getPredicateItem(index: number): Item | undefined;
|
|
220
|
+
/** Get mutable item for return values (returns ref or cached value) */
|
|
221
|
+
getMutableItem(index: number): MutableItem | undefined;
|
|
222
|
+
/** Insert with automatic conversion */
|
|
223
|
+
insertWithConversion(index: number, item: unknown): void;
|
|
224
|
+
/** Push with automatic conversion */
|
|
225
|
+
pushWithConversion(item: unknown): void;
|
|
226
|
+
/** Absorb value at specific index (for value shapes) - subclasses override */
|
|
227
|
+
absorbValueAtIndex(_index: number, _value: unknown): void;
|
|
228
|
+
/** Update cache indices after a delete operation */
|
|
229
|
+
updateCacheForDelete(deleteIndex: number, deleteLen: number): void;
|
|
230
|
+
/** Update cache indices after an insert operation */
|
|
231
|
+
updateCacheForInsert(insertIndex: number): void;
|
|
232
|
+
/** Absorb mutated plain values back into Loro containers */
|
|
91
233
|
absorbPlainValues(): void;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Returns the counter value.
|
|
96
|
-
* If the counter hasn't been materialized (no operations performed),
|
|
97
|
-
* returns the placeholder value if available.
|
|
98
|
-
*/
|
|
99
|
-
get value(): number;
|
|
100
|
-
valueOf(): number;
|
|
101
|
-
toJSON(): number;
|
|
102
|
-
[Symbol.toPrimitive](hint: string): number | string;
|
|
234
|
+
/** Create the loro namespace for list */
|
|
235
|
+
protected createLoroNamespace(): LoroListRef;
|
|
103
236
|
}
|
|
104
|
-
|
|
237
|
+
/**
|
|
238
|
+
* Shared logic for list operations - thin facade that delegates to ListRefBaseInternals.
|
|
239
|
+
*/
|
|
105
240
|
declare abstract class ListRefBase<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"], MutableItem = NestedShape["_mutable"]> extends TypedRef<any> {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
protected abstract absorbValueAtIndex(index: number, value: any): void;
|
|
111
|
-
protected insertWithConversion(index: number, item: Item): void;
|
|
112
|
-
protected pushWithConversion(item: Item): void;
|
|
113
|
-
getTypedRefParams(index: number, shape: ContainerShape): TypedRefParams<ContainerShape>;
|
|
114
|
-
protected getPredicateItem(index: number): Item;
|
|
115
|
-
protected getMutableItem(index: number): any;
|
|
241
|
+
[INTERNAL_SYMBOL]: ListRefBaseInternals<NestedShape, Item, MutableItem>;
|
|
242
|
+
constructor(params: TypedRefParams<any>);
|
|
243
|
+
/** Subclasses override to create their specific internals */
|
|
244
|
+
protected abstract createInternals(params: TypedRefParams<any>): ListRefBaseInternals<NestedShape, Item, MutableItem>;
|
|
116
245
|
find(predicate: (item: Item, index: number) => boolean): MutableItem | undefined;
|
|
117
246
|
findIndex(predicate: (item: Item, index: number) => boolean): number;
|
|
118
247
|
map<ReturnType>(callback: (item: Item, index: number) => ReturnType): ReturnType[];
|
|
@@ -131,104 +260,722 @@ declare abstract class ListRefBase<NestedShape extends ContainerOrValueShape, It
|
|
|
131
260
|
toJSON(): Item[];
|
|
132
261
|
[Symbol.iterator](): IterableIterator<MutableItem>;
|
|
133
262
|
get length(): number;
|
|
134
|
-
private updateCacheForDelete;
|
|
135
|
-
private updateCacheForInsert;
|
|
136
263
|
}
|
|
137
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Internal implementation for ListRef.
|
|
267
|
+
* Extends ListRefBaseInternals with LoroList-specific absorption logic.
|
|
268
|
+
*/
|
|
269
|
+
declare class ListRefInternals<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"], MutableItem = NestedShape["_mutable"]> extends ListRefBaseInternals<NestedShape, Item, MutableItem> {
|
|
270
|
+
/** Absorb value at specific index for LoroList */
|
|
271
|
+
absorbValueAtIndex(index: number, value: unknown): void;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* List typed ref - thin facade that delegates to ListRefInternals.
|
|
276
|
+
*/
|
|
138
277
|
declare class ListRef<NestedShape extends ContainerOrValueShape> extends ListRefBase<NestedShape> {
|
|
139
278
|
[index: number]: InferMutableType<NestedShape> | undefined;
|
|
140
|
-
protected
|
|
141
|
-
protected absorbValueAtIndex(index: number, value: any): void;
|
|
279
|
+
protected createInternals(params: TypedRefParams<any>): ListRefInternals<NestedShape>;
|
|
142
280
|
}
|
|
143
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Internal implementation for MovableListRef.
|
|
284
|
+
* Extends ListRefBaseInternals with LoroMovableList-specific methods.
|
|
285
|
+
*/
|
|
286
|
+
declare class MovableListRefInternals<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"], MutableItem = NestedShape["_mutable"]> extends ListRefBaseInternals<NestedShape, Item, MutableItem> {
|
|
287
|
+
/** Absorb value at specific index for LoroMovableList */
|
|
288
|
+
absorbValueAtIndex(index: number, value: unknown): void;
|
|
289
|
+
/** Move an item from one index to another */
|
|
290
|
+
move(from: number, to: number): void;
|
|
291
|
+
/** Set an item at a specific index */
|
|
292
|
+
set(index: number, item: unknown): void;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Movable list typed ref - thin facade that delegates to MovableListRefInternals.
|
|
297
|
+
*/
|
|
144
298
|
declare class MovableListRef<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"]> extends ListRefBase<NestedShape> {
|
|
299
|
+
[INTERNAL_SYMBOL]: MovableListRefInternals<NestedShape>;
|
|
145
300
|
[index: number]: InferMutableType<NestedShape> | undefined;
|
|
146
|
-
protected
|
|
147
|
-
protected absorbValueAtIndex(index: number, value: any): void;
|
|
301
|
+
protected createInternals(params: TypedRefParams<any>): MovableListRefInternals<NestedShape>;
|
|
148
302
|
move(from: number, to: number): void;
|
|
149
303
|
set(index: number, item: Exclude<Item, Container>): void;
|
|
150
304
|
}
|
|
151
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Internal implementation for RecordRef.
|
|
308
|
+
* Contains all logic, state, and implementation details.
|
|
309
|
+
*/
|
|
310
|
+
declare class RecordRefInternals<NestedShape extends ContainerOrValueShape> extends BaseRefInternals<any> {
|
|
311
|
+
private refCache;
|
|
312
|
+
/** Get typed ref params for creating child refs at a key */
|
|
313
|
+
getTypedRefParams(key: string, shape: ContainerShape): TypedRefParams<ContainerShape>;
|
|
314
|
+
/** Get a ref for a key without creating (returns undefined for non-existent container keys) */
|
|
315
|
+
getRef(key: string): unknown;
|
|
316
|
+
/** Get or create a ref for a key (always creates for container shapes) */
|
|
317
|
+
getOrCreateRef(key: string): unknown;
|
|
318
|
+
/** Set a value at a key */
|
|
319
|
+
set(key: string, value: any): void;
|
|
320
|
+
/** Delete a key */
|
|
321
|
+
delete(key: string): void;
|
|
322
|
+
/** Absorb mutated plain values back into Loro containers */
|
|
323
|
+
absorbPlainValues(): void;
|
|
324
|
+
/** Create the loro namespace for record */
|
|
325
|
+
protected createLoroNamespace(): LoroMapRef;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Record typed ref - thin facade that delegates to RecordRefInternals.
|
|
330
|
+
*/
|
|
152
331
|
declare class RecordRef<NestedShape extends ContainerOrValueShape> extends TypedRef<any> {
|
|
153
332
|
[key: string]: InferMutableType<NestedShape> | undefined | any;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
333
|
+
[INTERNAL_SYMBOL]: RecordRefInternals<NestedShape>;
|
|
334
|
+
constructor(params: TypedRefParams<any>);
|
|
335
|
+
/** Set a value at a key */
|
|
336
|
+
set(key: string, value: any): void;
|
|
337
|
+
/** Delete a key */
|
|
338
|
+
delete(key: string): void;
|
|
339
|
+
get(key: string): InferMutableType<NestedShape> | undefined;
|
|
340
|
+
setContainer<C extends Container>(key: string, container: C): C;
|
|
341
|
+
has(key: string): boolean;
|
|
342
|
+
keys(): string[];
|
|
343
|
+
values(): any[];
|
|
344
|
+
get size(): number;
|
|
345
|
+
toJSON(): Record<string, Infer<NestedShape>>;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Typed ref for struct containers (objects with fixed keys).
|
|
350
|
+
* Uses LoroMap as the underlying container.
|
|
351
|
+
*
|
|
352
|
+
* Supports JavaScript-native object behavior:
|
|
353
|
+
* - Property access: obj.key
|
|
354
|
+
* - Property assignment: obj.key = value
|
|
355
|
+
* - Object.keys(obj)
|
|
356
|
+
* - 'key' in obj
|
|
357
|
+
* - delete obj.key
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```typescript
|
|
361
|
+
* const schema = Shape.doc({
|
|
362
|
+
* settings: Shape.struct({
|
|
363
|
+
* darkMode: Shape.plain.boolean().placeholder(false),
|
|
364
|
+
* fontSize: Shape.plain.number().placeholder(14),
|
|
365
|
+
* }),
|
|
366
|
+
* });
|
|
367
|
+
*
|
|
368
|
+
* const doc = createTypedDoc(schema);
|
|
369
|
+
*
|
|
370
|
+
* // Property access
|
|
371
|
+
* doc.settings.darkMode = true;
|
|
372
|
+
* console.log(doc.settings.darkMode); // true
|
|
373
|
+
*
|
|
374
|
+
* // Object.keys()
|
|
375
|
+
* console.log(Object.keys(doc.settings)); // ['darkMode', 'fontSize']
|
|
376
|
+
*
|
|
377
|
+
* // 'key' in obj
|
|
378
|
+
* console.log('darkMode' in doc.settings); // true
|
|
379
|
+
*
|
|
380
|
+
* // delete obj.key
|
|
381
|
+
* delete doc.settings.darkMode;
|
|
382
|
+
*
|
|
383
|
+
* // CRDT access via loro()
|
|
384
|
+
* import { loro } from "@loro-extended/change";
|
|
385
|
+
* loro(doc.settings).setContainer('nested', loroMap);
|
|
386
|
+
* loro(doc.settings).subscribe(callback);
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
type StructRef<NestedShapes extends Record<string, ContainerOrValueShape>> = {
|
|
390
|
+
[K in keyof NestedShapes]: NestedShapes[K]["_mutable"];
|
|
391
|
+
} & {
|
|
392
|
+
/**
|
|
393
|
+
* Serializes the struct to a plain JSON-compatible object.
|
|
394
|
+
*/
|
|
395
|
+
toJSON(): Infer<StructContainerShape<NestedShapes>>;
|
|
396
|
+
/**
|
|
397
|
+
* Internal methods accessed via INTERNAL_SYMBOL.
|
|
398
|
+
* @internal
|
|
399
|
+
*/
|
|
400
|
+
[INTERNAL_SYMBOL]: RefInternalsBase;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Internal implementation for TextRef.
|
|
405
|
+
* Contains all logic, state, and implementation details.
|
|
406
|
+
*/
|
|
407
|
+
declare class TextRefInternals extends BaseRefInternals<TextContainerShape> {
|
|
408
|
+
private materialized;
|
|
409
|
+
/** Insert text at the given index */
|
|
410
|
+
insert(index: number, content: string): void;
|
|
411
|
+
/** Delete text at the given index */
|
|
412
|
+
delete(index: number, len: number): void;
|
|
413
|
+
/** Update the entire text content */
|
|
414
|
+
update(text: string): void;
|
|
415
|
+
/** Mark a range of text with a key-value pair */
|
|
416
|
+
mark(range: {
|
|
417
|
+
start: number;
|
|
418
|
+
end: number;
|
|
419
|
+
}, key: string, value: any): void;
|
|
420
|
+
/** Remove a mark from a range of text */
|
|
421
|
+
unmark(range: {
|
|
422
|
+
start: number;
|
|
423
|
+
end: number;
|
|
424
|
+
}, key: string): void;
|
|
425
|
+
/** Apply a delta to the text */
|
|
426
|
+
applyDelta(delta: any[]): void;
|
|
427
|
+
/** Get the text as a string */
|
|
428
|
+
getStringValue(): string;
|
|
429
|
+
/** Get the text as a delta */
|
|
430
|
+
toDelta(): any[];
|
|
431
|
+
/** Get the length of the text */
|
|
432
|
+
getLength(): number;
|
|
433
|
+
/** No plain values in text */
|
|
434
|
+
absorbPlainValues(): void;
|
|
435
|
+
/** Create the loro namespace for text */
|
|
436
|
+
protected createLoroNamespace(): LoroTextRef;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Text typed ref - thin facade that delegates to TextRefInternals.
|
|
441
|
+
*/
|
|
442
|
+
declare class TextRef extends TypedRef<TextContainerShape> {
|
|
443
|
+
[INTERNAL_SYMBOL]: TextRefInternals;
|
|
444
|
+
constructor(params: TypedRefParams<TextContainerShape>);
|
|
445
|
+
/** Insert text at the given index */
|
|
446
|
+
insert(index: number, content: string): void;
|
|
447
|
+
/** Delete text at the given index */
|
|
448
|
+
delete(index: number, len: number): void;
|
|
449
|
+
/** Update the entire text content */
|
|
450
|
+
update(text: string): void;
|
|
451
|
+
/** Mark a range of text with a key-value pair */
|
|
452
|
+
mark(range: {
|
|
453
|
+
start: number;
|
|
454
|
+
end: number;
|
|
455
|
+
}, key: string, value: any): void;
|
|
456
|
+
/** Remove a mark from a range of text */
|
|
457
|
+
unmark(range: {
|
|
458
|
+
start: number;
|
|
459
|
+
end: number;
|
|
460
|
+
}, key: string): void;
|
|
461
|
+
/** Apply a delta to the text */
|
|
462
|
+
applyDelta(delta: any[]): void;
|
|
463
|
+
/** Get the text as a string */
|
|
464
|
+
toString(): string;
|
|
465
|
+
valueOf(): string;
|
|
466
|
+
toJSON(): string;
|
|
467
|
+
[Symbol.toPrimitive](_hint: string): string;
|
|
468
|
+
/** Get the text as a delta */
|
|
469
|
+
toDelta(): any[];
|
|
470
|
+
/** Get the length of the text */
|
|
471
|
+
get length(): number;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
interface TreeRefLike<DataShape extends StructContainerShape> {
|
|
475
|
+
getOrCreateNodeRef(node: LoroTreeNode): TreeNodeRef<DataShape>;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Internal implementation for TreeNodeRef.
|
|
479
|
+
* Contains all logic, state, and implementation details.
|
|
480
|
+
*/
|
|
481
|
+
declare class TreeNodeRefInternals<DataShape extends StructContainerShape> implements RefInternalsBase {
|
|
482
|
+
private readonly params;
|
|
483
|
+
private dataRef;
|
|
484
|
+
constructor(params: TreeNodeRefParams<DataShape>);
|
|
485
|
+
/** Get the underlying LoroTreeNode */
|
|
486
|
+
getNode(): LoroTreeNode;
|
|
487
|
+
/** Get the data shape for this node */
|
|
488
|
+
getDataShape(): DataShape;
|
|
489
|
+
/** Get the parent TreeRef */
|
|
490
|
+
getTreeRef(): TreeRefLike<DataShape>;
|
|
491
|
+
/** Check if autoCommit is enabled */
|
|
492
|
+
getAutoCommit(): boolean;
|
|
493
|
+
/** Check if in batched mutation mode */
|
|
494
|
+
getBatchedMutation(): boolean;
|
|
495
|
+
/** Get the LoroDoc */
|
|
496
|
+
getDoc(): LoroDoc;
|
|
497
|
+
/** Commit changes if autoCommit is enabled */
|
|
498
|
+
commitIfAuto(): void;
|
|
499
|
+
/** Get or create the data StructRef */
|
|
500
|
+
getOrCreateDataRef(): StructRef<DataShape["shapes"]>;
|
|
501
|
+
/** Absorb mutated plain values back into Loro containers */
|
|
502
|
+
absorbPlainValues(): void;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
interface TreeNodeRefParams<DataShape extends StructContainerShape> {
|
|
506
|
+
node: LoroTreeNode;
|
|
507
|
+
dataShape: DataShape;
|
|
508
|
+
treeRef: TreeRefLike<DataShape>;
|
|
509
|
+
autoCommit?: boolean;
|
|
510
|
+
batchedMutation?: boolean;
|
|
511
|
+
getDoc: () => LoroDoc;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Typed ref for a single tree node - thin facade that delegates to TreeNodeRefInternals.
|
|
515
|
+
* Provides type-safe access to node metadata via the `.data` property.
|
|
516
|
+
*
|
|
517
|
+
* **Note:** TreeNodeRef is not a subclass of TypedRef, but it implements
|
|
518
|
+
* `[INTERNAL_SYMBOL]: RefInternalsBase` for consistency with other refs.
|
|
519
|
+
* This allows internal code to call `absorbPlainValues()` uniformly
|
|
520
|
+
* across all ref types during the `change()` commit phase.
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```typescript
|
|
524
|
+
* const node = tree.createNode({ name: "idle", facts: {} })
|
|
525
|
+
* node.data.name = "active" // Typed access
|
|
526
|
+
* const child = node.createNode({ name: "running", facts: {} })
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
declare class TreeNodeRef<DataShape extends StructContainerShape> {
|
|
530
|
+
[INTERNAL_SYMBOL]: TreeNodeRefInternals<DataShape>;
|
|
531
|
+
constructor(params: TreeNodeRefParams<DataShape>);
|
|
532
|
+
/**
|
|
533
|
+
* The unique TreeID of this node.
|
|
534
|
+
*/
|
|
535
|
+
get id(): TreeID;
|
|
536
|
+
/**
|
|
537
|
+
* Typed access to the node's metadata.
|
|
538
|
+
* This is a StructRef wrapping the node's LoroMap data container.
|
|
539
|
+
*/
|
|
540
|
+
get data(): StructRef<DataShape["shapes"]> & {
|
|
541
|
+
[K in keyof DataShape["shapes"]]: DataShape["shapes"][K]["_mutable"];
|
|
542
|
+
};
|
|
543
|
+
/**
|
|
544
|
+
* Create a child node under this node.
|
|
545
|
+
*
|
|
546
|
+
* @param initialData - Optional partial data to initialize the child with
|
|
547
|
+
* @param index - Optional position among siblings
|
|
548
|
+
* @returns The created child TreeNodeRef
|
|
549
|
+
*/
|
|
550
|
+
createNode(initialData?: Partial<Infer<DataShape>>, index?: number): TreeNodeRef<DataShape>;
|
|
551
|
+
/**
|
|
552
|
+
* Get the parent node, if any.
|
|
553
|
+
*/
|
|
554
|
+
parent(): TreeNodeRef<DataShape> | undefined;
|
|
555
|
+
/**
|
|
556
|
+
* Get all child nodes in order.
|
|
557
|
+
*/
|
|
558
|
+
children(): TreeNodeRef<DataShape>[];
|
|
559
|
+
/**
|
|
560
|
+
* Move this node to a new parent.
|
|
561
|
+
*
|
|
562
|
+
* @param newParent - The new parent node (undefined for root)
|
|
563
|
+
* @param index - Optional position among siblings
|
|
564
|
+
*/
|
|
565
|
+
move(newParent?: TreeNodeRef<DataShape>, index?: number): void;
|
|
566
|
+
/**
|
|
567
|
+
* Move this node to be after the given sibling.
|
|
568
|
+
*/
|
|
569
|
+
moveAfter(sibling: TreeNodeRef<DataShape>): void;
|
|
570
|
+
/**
|
|
571
|
+
* Move this node to be before the given sibling.
|
|
572
|
+
*/
|
|
573
|
+
moveBefore(sibling: TreeNodeRef<DataShape>): void;
|
|
574
|
+
/**
|
|
575
|
+
* Get the index of this node among its siblings.
|
|
576
|
+
*/
|
|
577
|
+
index(): number | undefined;
|
|
578
|
+
/**
|
|
579
|
+
* Get the fractional index string for ordering.
|
|
580
|
+
*/
|
|
581
|
+
fractionalIndex(): string | undefined;
|
|
582
|
+
/**
|
|
583
|
+
* Check if this node has been deleted.
|
|
584
|
+
*/
|
|
585
|
+
isDeleted(): boolean;
|
|
586
|
+
/**
|
|
587
|
+
* Serialize this node and its descendants to JSON.
|
|
588
|
+
*/
|
|
589
|
+
toJSON(): {
|
|
590
|
+
id: TreeID;
|
|
591
|
+
parent: TreeID | null;
|
|
592
|
+
index: number;
|
|
593
|
+
fractionalIndex: string;
|
|
594
|
+
data: Infer<DataShape>;
|
|
595
|
+
children: any[];
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Internal implementation for TreeRef.
|
|
601
|
+
* Contains all logic, state, and implementation details.
|
|
602
|
+
*/
|
|
603
|
+
declare class TreeRefInternals<DataShape extends StructContainerShape> extends BaseRefInternals<TreeContainerShape<DataShape>> {
|
|
604
|
+
private nodeCache;
|
|
605
|
+
private treeRef;
|
|
606
|
+
/** Set the parent TreeRef (needed for creating node refs) */
|
|
607
|
+
setTreeRef(treeRef: TreeRef<DataShape>): void;
|
|
608
|
+
/** Get the data shape for tree nodes */
|
|
609
|
+
getDataShape(): DataShape;
|
|
610
|
+
/** Get or create a node ref for a LoroTreeNode */
|
|
611
|
+
getOrCreateNodeRef(node: LoroTreeNode): TreeNodeRef<DataShape>;
|
|
612
|
+
/** Get a node by its ID */
|
|
613
|
+
getNodeByID(id: TreeID): TreeNodeRef<DataShape> | undefined;
|
|
614
|
+
/** Delete a node from the tree */
|
|
615
|
+
delete(target: TreeID | TreeNodeRef<DataShape>): void;
|
|
616
|
+
/** Absorb mutated plain values back into Loro containers */
|
|
617
|
+
absorbPlainValues(): void;
|
|
618
|
+
/** Create the loro namespace for tree */
|
|
619
|
+
protected createLoroNamespace(): LoroTreeRef;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Typed ref for tree (forest) containers - thin facade that delegates to TreeRefInternals.
|
|
624
|
+
* Wraps LoroTree with type-safe access to node metadata.
|
|
625
|
+
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```typescript
|
|
628
|
+
* const StateNodeDataShape = Shape.struct({
|
|
629
|
+
* name: Shape.text(),
|
|
630
|
+
* facts: Shape.record(Shape.plain.any()),
|
|
631
|
+
* })
|
|
632
|
+
*
|
|
633
|
+
* doc.change(draft => {
|
|
634
|
+
* const root = draft.states.createNode({ name: "idle", facts: {} })
|
|
635
|
+
* const child = root.createNode({ name: "running", facts: {} })
|
|
636
|
+
* child.data.name = "active"
|
|
637
|
+
* })
|
|
638
|
+
* ```
|
|
639
|
+
*/
|
|
640
|
+
declare class TreeRef<DataShape extends StructContainerShape> extends TypedRef<TreeContainerShape<DataShape>> {
|
|
641
|
+
[INTERNAL_SYMBOL]: TreeRefInternals<DataShape>;
|
|
642
|
+
constructor(params: TypedRefParams<TreeContainerShape<DataShape>>);
|
|
643
|
+
/**
|
|
644
|
+
* Get the data shape for tree nodes.
|
|
645
|
+
*/
|
|
646
|
+
private get dataShape();
|
|
647
|
+
/**
|
|
648
|
+
* Get or create a node ref for a LoroTreeNode.
|
|
649
|
+
*/
|
|
650
|
+
getOrCreateNodeRef(node: LoroTreeNode): TreeNodeRef<DataShape>;
|
|
651
|
+
/**
|
|
652
|
+
* Get a node by its ID.
|
|
653
|
+
*/
|
|
654
|
+
getNodeByID(id: TreeID): TreeNodeRef<DataShape> | undefined;
|
|
655
|
+
/**
|
|
656
|
+
* Delete a node from the tree.
|
|
657
|
+
*/
|
|
658
|
+
delete(target: TreeID | TreeNodeRef<DataShape>): void;
|
|
659
|
+
/**
|
|
660
|
+
* Serialize the tree to a nested JSON structure.
|
|
661
|
+
* Each node includes its data and children recursively.
|
|
662
|
+
*/
|
|
663
|
+
toJSON(): Infer<TreeContainerShape<DataShape>>;
|
|
664
|
+
/**
|
|
665
|
+
* Create a new root node with optional initial data.
|
|
666
|
+
*
|
|
667
|
+
* @param initialData - Optional partial data to initialize the node with
|
|
668
|
+
* @returns The created TreeNodeRef
|
|
669
|
+
*/
|
|
670
|
+
createNode(initialData?: Partial<Infer<DataShape>>): TreeNodeRef<DataShape>;
|
|
671
|
+
/**
|
|
672
|
+
* Get all root nodes (nodes without parents).
|
|
673
|
+
* Returns nodes in their fractional index order.
|
|
674
|
+
*/
|
|
675
|
+
roots(): TreeNodeRef<DataShape>[];
|
|
676
|
+
/**
|
|
677
|
+
* Get all nodes in the tree (unordered).
|
|
678
|
+
* Includes all nodes, not just roots.
|
|
679
|
+
*/
|
|
680
|
+
nodes(): TreeNodeRef<DataShape>[];
|
|
681
|
+
/**
|
|
682
|
+
* Check if a node with the given ID exists in the tree.
|
|
683
|
+
*/
|
|
684
|
+
has(id: TreeID): boolean;
|
|
685
|
+
/**
|
|
686
|
+
* Enable fractional index generation for ordering.
|
|
687
|
+
*
|
|
688
|
+
* @param jitter - Optional jitter value to avoid conflicts (0 = no jitter)
|
|
689
|
+
*/
|
|
690
|
+
enableFractionalIndex(jitter?: number): void;
|
|
691
|
+
/**
|
|
692
|
+
* Transform Loro's native JSON format to our typed format.
|
|
693
|
+
*/
|
|
694
|
+
private transformNativeJson;
|
|
695
|
+
/**
|
|
696
|
+
* Get a flat array representation of all nodes.
|
|
697
|
+
* Flattens the nested tree structure into a single array.
|
|
698
|
+
*/
|
|
699
|
+
toArray(): Array<{
|
|
700
|
+
id: TreeID;
|
|
701
|
+
parent: TreeID | null;
|
|
702
|
+
index: number;
|
|
703
|
+
fractionalIndex: string;
|
|
704
|
+
data: Infer<DataShape>;
|
|
705
|
+
}>;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* The `loro()` function - single escape hatch for CRDT internals.
|
|
710
|
+
*
|
|
711
|
+
* Design Principle:
|
|
712
|
+
* > If it takes a plain JavaScript value, keep it on the ref.
|
|
713
|
+
* > If it takes a Loro container or exposes CRDT internals, move to `loro()`.
|
|
714
|
+
*
|
|
715
|
+
* @example
|
|
716
|
+
* ```typescript
|
|
717
|
+
* import { loro } from "@loro-extended/change"
|
|
718
|
+
*
|
|
719
|
+
* // Access underlying LoroDoc
|
|
720
|
+
* loro(ref).doc
|
|
721
|
+
*
|
|
722
|
+
* // Access underlying Loro container (correctly typed)
|
|
723
|
+
* loro(ref).container // LoroList, LoroMap, LoroText, etc.
|
|
724
|
+
*
|
|
725
|
+
* // Subscribe to changes
|
|
726
|
+
* loro(ref).subscribe(callback)
|
|
727
|
+
*
|
|
728
|
+
* // Container operations
|
|
729
|
+
* loro(list).pushContainer(loroMap)
|
|
730
|
+
* loro(struct).setContainer('key', loroMap)
|
|
731
|
+
* ```
|
|
732
|
+
*/
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Well-known Symbol for loro() access.
|
|
736
|
+
* This is exported so advanced users can access it directly if needed.
|
|
737
|
+
*/
|
|
738
|
+
declare const LORO_SYMBOL: unique symbol;
|
|
739
|
+
/**
|
|
740
|
+
* Base interface for all loro() return types.
|
|
741
|
+
* Provides access to the underlying LoroDoc, container, and subscription.
|
|
742
|
+
*/
|
|
743
|
+
interface LoroRefBase {
|
|
744
|
+
/** The underlying LoroDoc */
|
|
745
|
+
readonly doc: LoroDoc;
|
|
746
|
+
/** The underlying Loro container */
|
|
747
|
+
readonly container: unknown;
|
|
748
|
+
/**
|
|
749
|
+
* Subscribe to container-level changes.
|
|
750
|
+
* @param callback - Function called when the container changes
|
|
751
|
+
* @returns Subscription that can be used to unsubscribe
|
|
752
|
+
*/
|
|
753
|
+
subscribe(callback: (event: unknown) => void): Subscription;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* loro() return type for ListRef and MovableListRef.
|
|
757
|
+
* Provides container operations that take Loro containers.
|
|
758
|
+
*/
|
|
759
|
+
interface LoroListRef extends LoroRefBase {
|
|
760
|
+
/** The underlying LoroList or LoroMovableList */
|
|
761
|
+
readonly container: LoroList | LoroMovableList;
|
|
762
|
+
/**
|
|
763
|
+
* Push a Loro container to the end of the list.
|
|
764
|
+
* Use this when you need to add a pre-existing container.
|
|
765
|
+
*/
|
|
766
|
+
pushContainer(container: Container): Container;
|
|
767
|
+
/**
|
|
768
|
+
* Insert a Loro container at the specified index.
|
|
769
|
+
* Use this when you need to insert a pre-existing container.
|
|
770
|
+
*/
|
|
771
|
+
insertContainer(index: number, container: Container): Container;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* loro() return type for StructRef and RecordRef.
|
|
775
|
+
* Provides container operations that take Loro containers.
|
|
776
|
+
*/
|
|
777
|
+
interface LoroMapRef extends LoroRefBase {
|
|
778
|
+
/** The underlying LoroMap */
|
|
779
|
+
readonly container: LoroMap;
|
|
780
|
+
/**
|
|
781
|
+
* Set a Loro container at the specified key.
|
|
782
|
+
* Use this when you need to set a pre-existing container.
|
|
783
|
+
*/
|
|
784
|
+
setContainer(key: string, container: Container): Container;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* loro() return type for TextRef.
|
|
788
|
+
*/
|
|
789
|
+
interface LoroTextRef extends LoroRefBase {
|
|
790
|
+
/** The underlying LoroText */
|
|
791
|
+
readonly container: LoroText;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* loro() return type for CounterRef.
|
|
795
|
+
*/
|
|
796
|
+
interface LoroCounterRef extends LoroRefBase {
|
|
797
|
+
/** The underlying LoroCounter */
|
|
798
|
+
readonly container: LoroCounter;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* loro() return type for TreeRef.
|
|
802
|
+
*/
|
|
803
|
+
interface LoroTreeRef extends LoroRefBase {
|
|
804
|
+
/** The underlying LoroTree */
|
|
805
|
+
readonly container: LoroTree;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* loro() return type for TypedDoc.
|
|
809
|
+
* Provides access to doc-level operations.
|
|
810
|
+
*/
|
|
811
|
+
interface LoroTypedDocRef extends LoroRefBase {
|
|
812
|
+
/** The underlying LoroDoc (same as doc for TypedDoc) */
|
|
813
|
+
readonly container: LoroDoc;
|
|
814
|
+
/**
|
|
815
|
+
* Apply JSON Patch operations to the document.
|
|
816
|
+
* @param patch - Array of JSON Patch operations (RFC 6902)
|
|
817
|
+
* @param pathPrefix - Optional path prefix for scoped operations
|
|
818
|
+
*/
|
|
819
|
+
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): void;
|
|
820
|
+
/** Access the document schema shape */
|
|
821
|
+
readonly docShape: DocShape;
|
|
822
|
+
/** Get raw CRDT value without placeholder overlay */
|
|
823
|
+
readonly rawValue: unknown;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Access CRDT internals for a ListRef.
|
|
827
|
+
*/
|
|
828
|
+
declare function loro<NestedShape extends ContainerShape>(ref: ListRef<NestedShape>): LoroListRef;
|
|
829
|
+
/**
|
|
830
|
+
* Access CRDT internals for a MovableListRef.
|
|
831
|
+
*/
|
|
832
|
+
declare function loro<NestedShape extends ContainerShape>(ref: MovableListRef<NestedShape>): LoroListRef;
|
|
833
|
+
/**
|
|
834
|
+
* Access CRDT internals for a StructRef.
|
|
835
|
+
*/
|
|
836
|
+
declare function loro<NestedShapes extends Record<string, ContainerOrValueShape>>(ref: StructRef<NestedShapes>): LoroMapRef;
|
|
837
|
+
/**
|
|
838
|
+
* Access CRDT internals for a RecordRef.
|
|
839
|
+
*/
|
|
840
|
+
declare function loro<NestedShape extends ContainerShape>(ref: RecordRef<NestedShape>): LoroMapRef;
|
|
841
|
+
/**
|
|
842
|
+
* Access CRDT internals for a TextRef.
|
|
843
|
+
*/
|
|
844
|
+
declare function loro(ref: TextRef): LoroTextRef;
|
|
845
|
+
/**
|
|
846
|
+
* Access CRDT internals for a CounterRef.
|
|
847
|
+
*/
|
|
848
|
+
declare function loro(ref: CounterRef): LoroCounterRef;
|
|
849
|
+
/**
|
|
850
|
+
* Access CRDT internals for a TreeRef.
|
|
851
|
+
*/
|
|
852
|
+
declare function loro<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>): LoroTreeRef;
|
|
853
|
+
/**
|
|
854
|
+
* Access CRDT internals for a TypedDoc.
|
|
855
|
+
*/
|
|
856
|
+
declare function loro<Shape extends DocShape>(doc: TypedDoc<Shape>): LoroTypedDocRef;
|
|
857
|
+
/**
|
|
858
|
+
* Access CRDT internals for any TypedRef.
|
|
859
|
+
*/
|
|
860
|
+
declare function loro<Shape extends ContainerShape>(ref: TypedRef<Shape>): LoroRefBase;
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* Symbol for internal methods that should not be enumerable or accessible to users.
|
|
864
|
+
* Used to hide implementation details like absorbPlainValues(), getTypedRefParams(), etc.
|
|
865
|
+
*
|
|
866
|
+
* This achieves Success Criteria #7 from loro-api-refactor.md:
|
|
867
|
+
* "Internal methods hidden - Via Symbol, not enumerable"
|
|
868
|
+
*/
|
|
869
|
+
declare const INTERNAL_SYMBOL: unique symbol;
|
|
870
|
+
/**
|
|
871
|
+
* Minimal interface for refs that only need absorbPlainValues.
|
|
872
|
+
* Used by TreeNodeRef which doesn't extend TypedRef.
|
|
873
|
+
*/
|
|
874
|
+
interface RefInternalsBase {
|
|
875
|
+
/** Absorb mutated plain values back into Loro containers */
|
|
157
876
|
absorbPlainValues(): void;
|
|
158
|
-
|
|
877
|
+
}
|
|
878
|
+
type TypedRefParams<Shape extends DocShape | ContainerShape> = {
|
|
879
|
+
shape: Shape;
|
|
880
|
+
placeholder?: Infer<Shape>;
|
|
881
|
+
getContainer: () => ShapeToContainer<Shape>;
|
|
882
|
+
autoCommit?: boolean;
|
|
883
|
+
batchedMutation?: boolean;
|
|
884
|
+
getDoc: () => LoroDoc;
|
|
885
|
+
};
|
|
886
|
+
/**
|
|
887
|
+
* Abstract base class for all ref internal implementations.
|
|
888
|
+
* Contains shared logic that was previously in TypedRef.createBaseInternals().
|
|
889
|
+
*
|
|
890
|
+
* Subclasses implement specific behavior for each ref type.
|
|
891
|
+
*/
|
|
892
|
+
declare abstract class BaseRefInternals<Shape extends DocShape | ContainerShape> implements RefInternalsBase {
|
|
893
|
+
protected readonly params: TypedRefParams<Shape>;
|
|
894
|
+
protected cachedContainer: ShapeToContainer<Shape> | undefined;
|
|
895
|
+
protected loroNamespace: LoroRefBase | undefined;
|
|
896
|
+
constructor(params: TypedRefParams<Shape>);
|
|
897
|
+
/** Get the underlying Loro container (cached) */
|
|
898
|
+
getContainer(): ShapeToContainer<Shape>;
|
|
899
|
+
/** Commit changes if autoCommit is enabled */
|
|
900
|
+
commitIfAuto(): void;
|
|
901
|
+
/** Get the shape for this ref */
|
|
902
|
+
getShape(): Shape;
|
|
903
|
+
/** Get the placeholder value */
|
|
904
|
+
getPlaceholder(): Infer<Shape> | undefined;
|
|
905
|
+
/** Check if autoCommit is enabled */
|
|
906
|
+
getAutoCommit(): boolean;
|
|
907
|
+
/** Check if in batched mutation mode */
|
|
908
|
+
getBatchedMutation(): boolean;
|
|
909
|
+
/** Get the LoroDoc */
|
|
910
|
+
getDoc(): LoroDoc;
|
|
911
|
+
/** Get the loro namespace (cached) */
|
|
912
|
+
getLoroNamespace(): LoroRefBase;
|
|
913
|
+
/** Absorb mutated plain values back into Loro containers - subclasses override */
|
|
914
|
+
abstract absorbPlainValues(): void;
|
|
915
|
+
/** Create the loro() namespace object - subclasses override for specific types */
|
|
916
|
+
protected createLoroNamespace(): LoroRefBase;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Base class for all typed refs.
|
|
920
|
+
*
|
|
921
|
+
* All internal methods are accessed via [INTERNAL_SYMBOL] to prevent
|
|
922
|
+
* namespace collisions with user data properties.
|
|
923
|
+
*
|
|
924
|
+
* Uses the Facade + Implementation pattern:
|
|
925
|
+
* - TypedRef is the thin public facade
|
|
926
|
+
* - BaseRefInternals subclasses contain all implementation logic
|
|
927
|
+
*/
|
|
928
|
+
declare abstract class TypedRef<Shape extends DocShape | ContainerShape> {
|
|
159
929
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
930
|
+
* Internal implementation accessed via Symbol.
|
|
931
|
+
* Subclasses must set this to their specific internals class instance.
|
|
162
932
|
*/
|
|
163
|
-
|
|
933
|
+
abstract [INTERNAL_SYMBOL]: BaseRefInternals<Shape>;
|
|
164
934
|
/**
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* This is the method used for write operations.
|
|
935
|
+
* Serializes the ref to a plain JSON-compatible value.
|
|
936
|
+
* Returns the plain type inferred from the shape.
|
|
168
937
|
*/
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
keys(): string[];
|
|
176
|
-
values(): any[];
|
|
177
|
-
get size(): number;
|
|
178
|
-
toJSON(): Record<string, Infer<NestedShape>>;
|
|
938
|
+
abstract toJSON(): Infer<Shape>;
|
|
939
|
+
/**
|
|
940
|
+
* Access the loro() namespace via the well-known symbol.
|
|
941
|
+
* This is used by the loro() function to access CRDT internals.
|
|
942
|
+
*/
|
|
943
|
+
get [LORO_SYMBOL](): LoroRefBase;
|
|
179
944
|
}
|
|
180
945
|
|
|
181
946
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
947
|
+
* Internal implementation for CounterRef.
|
|
948
|
+
* Contains all logic, state, and implementation details.
|
|
184
949
|
*/
|
|
185
|
-
declare class
|
|
186
|
-
private
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
950
|
+
declare class CounterRefInternals extends BaseRefInternals<CounterContainerShape> {
|
|
951
|
+
private materialized;
|
|
952
|
+
/** Increment the counter value */
|
|
953
|
+
increment(value?: number): void;
|
|
954
|
+
/** Decrement the counter value */
|
|
955
|
+
decrement(value?: number): void;
|
|
956
|
+
/** Get the current counter value */
|
|
957
|
+
getValue(): number;
|
|
958
|
+
/** No plain values in counter */
|
|
190
959
|
absorbPlainValues(): void;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
private createLazyProperties;
|
|
194
|
-
toJSON(): Infer<StructContainerShape<NestedShapes>>;
|
|
195
|
-
get(key: string): any;
|
|
196
|
-
set(key: string, value: Value): void;
|
|
197
|
-
setContainer<C extends Container>(key: string, container: C): C;
|
|
198
|
-
delete(key: string): void;
|
|
199
|
-
has(key: string): boolean;
|
|
200
|
-
keys(): string[];
|
|
201
|
-
values(): any[];
|
|
202
|
-
get size(): number;
|
|
960
|
+
/** Create the loro namespace for counter */
|
|
961
|
+
protected createLoroNamespace(): LoroCounterRef;
|
|
203
962
|
}
|
|
204
963
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
valueOf():
|
|
218
|
-
toJSON():
|
|
219
|
-
[Symbol.toPrimitive](
|
|
220
|
-
update(text: string): void;
|
|
221
|
-
mark(range: {
|
|
222
|
-
start: number;
|
|
223
|
-
end: number;
|
|
224
|
-
}, key: string, value: any): void;
|
|
225
|
-
unmark(range: {
|
|
226
|
-
start: number;
|
|
227
|
-
end: number;
|
|
228
|
-
}, key: string): void;
|
|
229
|
-
toDelta(): any[];
|
|
230
|
-
applyDelta(delta: any[]): void;
|
|
231
|
-
get length(): number;
|
|
964
|
+
/**
|
|
965
|
+
* Counter typed ref - thin facade that delegates to CounterRefInternals.
|
|
966
|
+
*/
|
|
967
|
+
declare class CounterRef extends TypedRef<CounterContainerShape> {
|
|
968
|
+
[INTERNAL_SYMBOL]: CounterRefInternals;
|
|
969
|
+
constructor(params: TypedRefParams<CounterContainerShape>);
|
|
970
|
+
/** Increment the counter by the given value (default 1) */
|
|
971
|
+
increment(value?: number): void;
|
|
972
|
+
/** Decrement the counter by the given value (default 1) */
|
|
973
|
+
decrement(value?: number): void;
|
|
974
|
+
/** Get the current counter value */
|
|
975
|
+
get value(): number;
|
|
976
|
+
valueOf(): number;
|
|
977
|
+
toJSON(): number;
|
|
978
|
+
[Symbol.toPrimitive](hint: string): number | string;
|
|
232
979
|
}
|
|
233
980
|
|
|
234
981
|
type WithPlaceholder<S extends Shape<any, any, any>> = S & {
|
|
@@ -257,9 +1004,74 @@ interface TextContainerShape extends Shape<string, TextRef, string> {
|
|
|
257
1004
|
interface CounterContainerShape extends Shape<number, CounterRef, number> {
|
|
258
1005
|
readonly _type: "counter";
|
|
259
1006
|
}
|
|
260
|
-
|
|
1007
|
+
/**
|
|
1008
|
+
* JSON representation of a tree node with typed data.
|
|
1009
|
+
* Used for serialization (toJSON) of tree structures.
|
|
1010
|
+
*/
|
|
1011
|
+
type TreeNodeJSON<DataShape extends StructContainerShape> = {
|
|
1012
|
+
id: TreeID;
|
|
1013
|
+
parent: TreeID | null;
|
|
1014
|
+
index: number;
|
|
1015
|
+
fractionalIndex: string;
|
|
1016
|
+
data: DataShape["_plain"];
|
|
1017
|
+
children: TreeNodeJSON<DataShape>[];
|
|
1018
|
+
};
|
|
1019
|
+
/**
|
|
1020
|
+
* Interface describing the TreeRef API for use in shape definitions.
|
|
1021
|
+
* This avoids circular type references that would occur with the TreeRef class.
|
|
1022
|
+
* @internal
|
|
1023
|
+
*/
|
|
1024
|
+
interface TreeRefInterface<DataShape extends StructContainerShape> {
|
|
1025
|
+
/** Get or create a node ref for a LoroTreeNode */
|
|
1026
|
+
getOrCreateNodeRef(node: unknown): TreeNodeRef<DataShape>;
|
|
1027
|
+
/** Get a node by its ID */
|
|
1028
|
+
getNodeByID(id: TreeID): TreeNodeRef<DataShape> | undefined;
|
|
1029
|
+
/** Delete a node from the tree */
|
|
1030
|
+
delete(target: TreeID | TreeNodeRef<DataShape>): void;
|
|
1031
|
+
/** Serialize the tree to a nested JSON structure */
|
|
1032
|
+
toJSON(): TreeNodeJSON<DataShape>[];
|
|
1033
|
+
/** Create a new root node with optional initial data */
|
|
1034
|
+
createNode(initialData?: Partial<DataShape["_plain"]>): TreeNodeRef<DataShape>;
|
|
1035
|
+
/** Get all root nodes (nodes without parents) */
|
|
1036
|
+
roots(): TreeNodeRef<DataShape>[];
|
|
1037
|
+
/** Get all nodes in the tree (unordered) */
|
|
1038
|
+
nodes(): TreeNodeRef<DataShape>[];
|
|
1039
|
+
/** Check if a node with the given ID exists in the tree */
|
|
1040
|
+
has(id: TreeID): boolean;
|
|
1041
|
+
/** Enable fractional index generation for ordering */
|
|
1042
|
+
enableFractionalIndex(jitter?: number): void;
|
|
1043
|
+
/** Get a flat array representation of all nodes */
|
|
1044
|
+
toArray(): Array<{
|
|
1045
|
+
id: TreeID;
|
|
1046
|
+
parent: TreeID | null;
|
|
1047
|
+
index: number;
|
|
1048
|
+
fractionalIndex: string;
|
|
1049
|
+
data: DataShape["_plain"];
|
|
1050
|
+
}>;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Container shape for tree (forest) structures.
|
|
1054
|
+
* Each node in the tree has typed metadata stored in a LoroMap.
|
|
1055
|
+
*
|
|
1056
|
+
* @example
|
|
1057
|
+
* ```typescript
|
|
1058
|
+
* const StateNodeDataShape = Shape.struct({
|
|
1059
|
+
* name: Shape.text(),
|
|
1060
|
+
* facts: Shape.record(Shape.plain.any()),
|
|
1061
|
+
* })
|
|
1062
|
+
*
|
|
1063
|
+
* const Schema = Shape.doc({
|
|
1064
|
+
* states: Shape.tree(StateNodeDataShape),
|
|
1065
|
+
* })
|
|
1066
|
+
* ```
|
|
1067
|
+
*/
|
|
1068
|
+
interface TreeContainerShape<DataShape extends StructContainerShape = StructContainerShape> extends Shape<TreeNodeJSON<DataShape>[], TreeRefInterface<DataShape>, never[]> {
|
|
261
1069
|
readonly _type: "tree";
|
|
262
|
-
|
|
1070
|
+
/**
|
|
1071
|
+
* The shape of each node's data (metadata).
|
|
1072
|
+
* This is a StructContainerShape that defines the typed properties on node.data.
|
|
1073
|
+
*/
|
|
1074
|
+
readonly shape: DataShape;
|
|
263
1075
|
}
|
|
264
1076
|
interface ListContainerShape<NestedShape extends ContainerOrValueShape = ContainerOrValueShape> extends Shape<NestedShape["_plain"][], ListRef<NestedShape>, never[]> {
|
|
265
1077
|
readonly _type: "list";
|
|
@@ -460,7 +1272,29 @@ declare const Shape: {
|
|
|
460
1272
|
record: <T extends ContainerOrValueShape>(shape: T) => RecordContainerShape<T>;
|
|
461
1273
|
movableList: <T extends ContainerOrValueShape>(shape: T) => MovableListContainerShape<T>;
|
|
462
1274
|
text: () => WithPlaceholder<TextContainerShape>;
|
|
463
|
-
|
|
1275
|
+
/**
|
|
1276
|
+
* Creates a tree container shape for hierarchical data structures.
|
|
1277
|
+
* Each node in the tree has typed metadata defined by the data shape.
|
|
1278
|
+
*
|
|
1279
|
+
* @example
|
|
1280
|
+
* ```typescript
|
|
1281
|
+
* const StateNodeDataShape = Shape.struct({
|
|
1282
|
+
* name: Shape.text(),
|
|
1283
|
+
* facts: Shape.record(Shape.plain.any()),
|
|
1284
|
+
* })
|
|
1285
|
+
*
|
|
1286
|
+
* const Schema = Shape.doc({
|
|
1287
|
+
* states: Shape.tree(StateNodeDataShape),
|
|
1288
|
+
* })
|
|
1289
|
+
*
|
|
1290
|
+
* doc.change(draft => {
|
|
1291
|
+
* const root = draft.states.createNode({ name: "idle", facts: {} })
|
|
1292
|
+
* const child = root.createNode({ name: "running", facts: {} })
|
|
1293
|
+
* child.data.name = "active"
|
|
1294
|
+
* })
|
|
1295
|
+
* ```
|
|
1296
|
+
*/
|
|
1297
|
+
tree: <T extends StructContainerShape>(shape: T) => TreeContainerShape<T>;
|
|
464
1298
|
plain: {
|
|
465
1299
|
string: <T extends string = string>(...options: T[]) => WithPlaceholder<StringValueShape<T>> & WithNullable<StringValueShape<T>>;
|
|
466
1300
|
number: () => WithPlaceholder<NumberValueShape> & WithNullable<NumberValueShape>;
|
|
@@ -559,180 +1393,6 @@ declare function derivePlaceholder<T extends DocShape>(schema: T): InferPlacehol
|
|
|
559
1393
|
*/
|
|
560
1394
|
declare function deriveShapePlaceholder(shape: ContainerOrValueShape): unknown;
|
|
561
1395
|
|
|
562
|
-
/** biome-ignore-all lint/suspicious/noExplicitAny: JSON Patch values can be any type */
|
|
563
|
-
|
|
564
|
-
type JsonPatchAddOperation = {
|
|
565
|
-
op: "add";
|
|
566
|
-
path: string | (string | number)[];
|
|
567
|
-
value: any;
|
|
568
|
-
};
|
|
569
|
-
type JsonPatchRemoveOperation = {
|
|
570
|
-
op: "remove";
|
|
571
|
-
path: string | (string | number)[];
|
|
572
|
-
};
|
|
573
|
-
type JsonPatchReplaceOperation = {
|
|
574
|
-
op: "replace";
|
|
575
|
-
path: string | (string | number)[];
|
|
576
|
-
value: any;
|
|
577
|
-
};
|
|
578
|
-
type JsonPatchMoveOperation = {
|
|
579
|
-
op: "move";
|
|
580
|
-
path: string | (string | number)[];
|
|
581
|
-
from: string | (string | number)[];
|
|
582
|
-
};
|
|
583
|
-
type JsonPatchCopyOperation = {
|
|
584
|
-
op: "copy";
|
|
585
|
-
path: string | (string | number)[];
|
|
586
|
-
from: string | (string | number)[];
|
|
587
|
-
};
|
|
588
|
-
type JsonPatchTestOperation = {
|
|
589
|
-
op: "test";
|
|
590
|
-
path: string | (string | number)[];
|
|
591
|
-
value: any;
|
|
592
|
-
};
|
|
593
|
-
type JsonPatchOperation = JsonPatchAddOperation | JsonPatchRemoveOperation | JsonPatchReplaceOperation | JsonPatchMoveOperation | JsonPatchCopyOperation | JsonPatchTestOperation;
|
|
594
|
-
type JsonPatch = JsonPatchOperation[];
|
|
595
|
-
|
|
596
|
-
/** biome-ignore-all lint/suspicious/noExplicitAny: fix later */
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Meta-operations namespace for TypedDoc.
|
|
600
|
-
* Access via doc.$ to perform batch operations, serialization, etc.
|
|
601
|
-
*/
|
|
602
|
-
declare class TypedDocMeta<Shape extends DocShape> {
|
|
603
|
-
private internal;
|
|
604
|
-
constructor(internal: TypedDocInternal<Shape>);
|
|
605
|
-
/**
|
|
606
|
-
* The primary method of mutating typed documents.
|
|
607
|
-
* Batches multiple mutations into a single transaction.
|
|
608
|
-
* All changes commit together at the end.
|
|
609
|
-
*
|
|
610
|
-
* Use this for:
|
|
611
|
-
* - Find-and-mutate operations (required due to JS limitations)
|
|
612
|
-
* - Performance (fewer commits)
|
|
613
|
-
* - Atomic undo (all changes = one undo step)
|
|
614
|
-
*
|
|
615
|
-
* Returns the doc for chaining.
|
|
616
|
-
*/
|
|
617
|
-
change(fn: (draft: Mutable<Shape>) => void): TypedDoc<Shape>;
|
|
618
|
-
/**
|
|
619
|
-
* Returns the full plain JavaScript object representation of the document.
|
|
620
|
-
* This is an expensive O(N) operation that serializes the entire document.
|
|
621
|
-
*/
|
|
622
|
-
toJSON(): Infer<Shape>;
|
|
623
|
-
/**
|
|
624
|
-
* Apply JSON Patch operations to the document
|
|
625
|
-
*
|
|
626
|
-
* @param patch - Array of JSON Patch operations (RFC 6902)
|
|
627
|
-
* @param pathPrefix - Optional path prefix for scoped operations
|
|
628
|
-
* @returns Updated document value
|
|
629
|
-
*/
|
|
630
|
-
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): TypedDoc<Shape>;
|
|
631
|
-
/**
|
|
632
|
-
* Access the underlying LoroDoc for advanced operations.
|
|
633
|
-
*/
|
|
634
|
-
get loroDoc(): LoroDoc;
|
|
635
|
-
/**
|
|
636
|
-
* Access the document schema shape.
|
|
637
|
-
*/
|
|
638
|
-
get docShape(): Shape;
|
|
639
|
-
/**
|
|
640
|
-
* Get raw CRDT value without placeholder overlay.
|
|
641
|
-
*/
|
|
642
|
-
get rawValue(): any;
|
|
643
|
-
}
|
|
644
|
-
/**
|
|
645
|
-
* Internal TypedDoc implementation (not directly exposed to users).
|
|
646
|
-
* Users interact with the proxied version that provides direct schema access.
|
|
647
|
-
*/
|
|
648
|
-
declare class TypedDocInternal<Shape extends DocShape> {
|
|
649
|
-
private shape;
|
|
650
|
-
private placeholder;
|
|
651
|
-
private doc;
|
|
652
|
-
private _valueRef;
|
|
653
|
-
proxy: TypedDoc<Shape> | null;
|
|
654
|
-
constructor(shape: Shape, doc?: LoroDoc);
|
|
655
|
-
get value(): Mutable<Shape>;
|
|
656
|
-
toJSON(): Infer<Shape>;
|
|
657
|
-
change(fn: (draft: Mutable<Shape>) => void): void;
|
|
658
|
-
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): void;
|
|
659
|
-
get loroDoc(): LoroDoc;
|
|
660
|
-
get docShape(): Shape;
|
|
661
|
-
get rawValue(): any;
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* The proxied TypedDoc type that provides direct schema access.
|
|
665
|
-
* Schema properties are accessed directly on the doc object.
|
|
666
|
-
* Meta-operations are available via the $ namespace.
|
|
667
|
-
*
|
|
668
|
-
* @example
|
|
669
|
-
* ```typescript
|
|
670
|
-
* const doc = createTypedDoc(schema);
|
|
671
|
-
*
|
|
672
|
-
* // Direct schema access
|
|
673
|
-
* doc.count.increment(5);
|
|
674
|
-
* doc.title.insert(0, "Hello");
|
|
675
|
-
*
|
|
676
|
-
* // Serialize to JSON (works on doc and all refs)
|
|
677
|
-
* const snapshot = doc.toJSON();
|
|
678
|
-
* const users = doc.users.toJSON();
|
|
679
|
-
*
|
|
680
|
-
* // Meta-operations via $ (escape hatch)
|
|
681
|
-
* doc.$.change(draft => { ... });
|
|
682
|
-
* doc.$.loroDoc;
|
|
683
|
-
* ```
|
|
684
|
-
*/
|
|
685
|
-
type TypedDoc<Shape extends DocShape> = Mutable<Shape> & {
|
|
686
|
-
/**
|
|
687
|
-
* Meta-operations namespace.
|
|
688
|
-
* Use for change(), loroDoc, etc.
|
|
689
|
-
*/
|
|
690
|
-
$: TypedDocMeta<Shape>;
|
|
691
|
-
/**
|
|
692
|
-
* Returns the full plain JavaScript object representation of the document.
|
|
693
|
-
* This is an O(N) operation that serializes the entire document.
|
|
694
|
-
*
|
|
695
|
-
* @example
|
|
696
|
-
* ```typescript
|
|
697
|
-
* const snapshot = doc.toJSON();
|
|
698
|
-
* console.log(snapshot.count); // number
|
|
699
|
-
* ```
|
|
700
|
-
*/
|
|
701
|
-
toJSON(): Infer<Shape>;
|
|
702
|
-
};
|
|
703
|
-
/**
|
|
704
|
-
* Creates a new TypedDoc with the given schema.
|
|
705
|
-
* Returns a proxied document where schema properties are accessed directly.
|
|
706
|
-
*
|
|
707
|
-
* @param shape - The document schema (with optional .placeholder() values)
|
|
708
|
-
* @param existingDoc - Optional existing LoroDoc to wrap
|
|
709
|
-
* @returns A proxied TypedDoc with direct schema access and $ namespace
|
|
710
|
-
*
|
|
711
|
-
* @example
|
|
712
|
-
* ```typescript
|
|
713
|
-
* const schema = Shape.doc({
|
|
714
|
-
* title: Shape.text(),
|
|
715
|
-
* count: Shape.counter(),
|
|
716
|
-
* });
|
|
717
|
-
*
|
|
718
|
-
* const doc = createTypedDoc(schema);
|
|
719
|
-
*
|
|
720
|
-
* // Direct mutations (auto-commit)
|
|
721
|
-
* doc.count.increment(5);
|
|
722
|
-
* doc.title.insert(0, "Hello");
|
|
723
|
-
*
|
|
724
|
-
* // Batched mutations are committed together via `change`
|
|
725
|
-
* doc.$.change(draft => {
|
|
726
|
-
* draft.count.increment(10);
|
|
727
|
-
* draft.title.update("World");
|
|
728
|
-
* });
|
|
729
|
-
*
|
|
730
|
-
* // Get plain JSON
|
|
731
|
-
* const snapshot = doc.toJSON();
|
|
732
|
-
* ```
|
|
733
|
-
*/
|
|
734
|
-
declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDoc?: LoroDoc): TypedDoc<Shape>;
|
|
735
|
-
|
|
736
1396
|
/**
|
|
737
1397
|
* The primary method of mutating typed documents.
|
|
738
1398
|
* Batches multiple mutations into a single transaction.
|
|
@@ -765,20 +1425,62 @@ declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDo
|
|
|
765
1425
|
declare function change<Shape extends DocShape>(doc: TypedDoc<Shape>, fn: (draft: Mutable<Shape>) => void): TypedDoc<Shape>;
|
|
766
1426
|
/**
|
|
767
1427
|
* Access the underlying LoroDoc for advanced operations.
|
|
1428
|
+
* Works on both TypedDoc and any typed ref (TextRef, CounterRef, ListRef, etc.).
|
|
768
1429
|
*
|
|
769
|
-
* @param
|
|
770
|
-
* @returns The underlying LoroDoc instance
|
|
1430
|
+
* @param docOrRef - The TypedDoc or typed ref to unwrap
|
|
1431
|
+
* @returns The underlying LoroDoc instance (or undefined for refs created outside a doc context)
|
|
771
1432
|
*
|
|
772
1433
|
* @example
|
|
773
1434
|
* ```typescript
|
|
774
1435
|
* import { getLoroDoc } from "@loro-extended/change"
|
|
775
1436
|
*
|
|
1437
|
+
* // From TypedDoc
|
|
776
1438
|
* const loroDoc = getLoroDoc(doc)
|
|
777
1439
|
* const version = loroDoc.version()
|
|
778
1440
|
* loroDoc.subscribe(() => console.log("changed"))
|
|
1441
|
+
*
|
|
1442
|
+
* // From any ref (TextRef, CounterRef, ListRef, etc.)
|
|
1443
|
+
* const titleRef = doc.title
|
|
1444
|
+
* const loroDoc = getLoroDoc(titleRef)
|
|
1445
|
+
* loroDoc?.subscribe(() => console.log("changed"))
|
|
779
1446
|
* ```
|
|
780
1447
|
*/
|
|
781
1448
|
declare function getLoroDoc<Shape extends DocShape>(doc: TypedDoc<Shape>): LoroDoc;
|
|
1449
|
+
declare function getLoroDoc<Shape extends ContainerShape>(ref: TypedRef<Shape>): LoroDoc;
|
|
1450
|
+
declare function getLoroDoc<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>): LoroDoc;
|
|
1451
|
+
/**
|
|
1452
|
+
* Access the underlying Loro container from a typed ref.
|
|
1453
|
+
* Returns the correctly-typed container based on the ref type.
|
|
1454
|
+
*
|
|
1455
|
+
* @param ref - The typed ref to unwrap
|
|
1456
|
+
* @returns The underlying Loro container (LoroText, LoroCounter, LoroList, etc.)
|
|
1457
|
+
*
|
|
1458
|
+
* @example
|
|
1459
|
+
* ```typescript
|
|
1460
|
+
* import { getLoroContainer } from "@loro-extended/change"
|
|
1461
|
+
*
|
|
1462
|
+
* const titleRef = doc.title
|
|
1463
|
+
* const loroText = getLoroContainer(titleRef) // LoroText
|
|
1464
|
+
*
|
|
1465
|
+
* const countRef = doc.count
|
|
1466
|
+
* const loroCounter = getLoroContainer(countRef) // LoroCounter
|
|
1467
|
+
*
|
|
1468
|
+
* const itemsRef = doc.items
|
|
1469
|
+
* const loroList = getLoroContainer(itemsRef) // LoroList
|
|
1470
|
+
*
|
|
1471
|
+
* // Subscribe to container-level changes
|
|
1472
|
+
* loroText.subscribe((event) => console.log("Text changed:", event))
|
|
1473
|
+
* ```
|
|
1474
|
+
*/
|
|
1475
|
+
declare function getLoroContainer(ref: TypedRef<TextContainerShape>): LoroText;
|
|
1476
|
+
declare function getLoroContainer(ref: TypedRef<CounterContainerShape>): LoroCounter;
|
|
1477
|
+
declare function getLoroContainer(ref: TypedRef<ListContainerShape>): LoroList;
|
|
1478
|
+
declare function getLoroContainer(ref: TypedRef<MovableListContainerShape>): LoroMovableList;
|
|
1479
|
+
declare function getLoroContainer(ref: TypedRef<RecordContainerShape>): LoroMap;
|
|
1480
|
+
declare function getLoroContainer(ref: TypedRef<StructContainerShape>): LoroMap;
|
|
1481
|
+
declare function getLoroContainer<NestedShapes extends Record<string, ContainerOrValueShape>>(ref: StructRef<NestedShapes>): LoroMap;
|
|
1482
|
+
declare function getLoroContainer<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>): LoroTree;
|
|
1483
|
+
declare function getLoroContainer<Shape extends ContainerShape>(ref: TypedRef<Shape>): ShapeToContainer<Shape>;
|
|
782
1484
|
|
|
783
1485
|
/**
|
|
784
1486
|
* Overlays CRDT state with placeholder defaults
|
|
@@ -921,4 +1623,4 @@ declare function createPlaceholderProxy<T extends object>(target: T): T;
|
|
|
921
1623
|
*/
|
|
922
1624
|
declare function validatePlaceholder<T extends DocShape>(placeholder: unknown, schema: T): Infer<T>;
|
|
923
1625
|
|
|
924
|
-
export { type AnyContainerShape, type AnyValueShape, type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type DiscriminatedUnionValueShape, type DocShape, type Infer, type InferMutableType, type InferPlaceholderType, type ListContainerShape, type MapContainerShape, type MovableListContainerShape, type Mutable, type ObjectValueShape, type PathBuilder, type PathNode, type PathSegment, type PathSelector, type RecordContainerShape, type RecordValueShape, type ContainerType as RootContainerType, Shape, type StructContainerShape, type StructValueShape, type TextContainerShape, type TreeContainerShape, type TypedDoc, type UnionValueShape, type ValueShape, type WithNullable, type WithPlaceholder, change, compileToJsonPath, createPathBuilder, createPlaceholderProxy, createTypedDoc, derivePlaceholder, deriveShapePlaceholder, evaluatePath, evaluatePathOnValue, getLoroDoc, hasWildcard, mergeValue, overlayPlaceholder, validatePlaceholder };
|
|
1626
|
+
export { type AnyContainerShape, type AnyValueShape, type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, CounterRef, type DiscriminatedUnionValueShape, type DocShape, type Infer, type InferMutableType, type InferPlaceholderType, type InferRaw, LORO_SYMBOL, type ListContainerShape, ListRef, type LoroCounterRef, type LoroListRef, type LoroMapRef, type LoroRefBase, type LoroTextRef, type LoroTreeRef, type LoroTypedDocRef, type MapContainerShape, type MovableListContainerShape, MovableListRef, type Mutable, type ObjectValueShape, type PathBuilder, type PathNode, type PathSegment, type PathSelector, type RecordContainerShape, RecordRef, type RecordValueShape, type ContainerType as RootContainerType, Shape, type StructContainerShape, type StructRef, type StructValueShape, type TextContainerShape, TextRef, type TreeContainerShape, type TreeNodeJSON, TreeNodeRef, TreeRef, type TreeRefInterface, type TypedDoc, type UnionValueShape, type ValueShape, type WithNullable, type WithPlaceholder, change, compileToJsonPath, createPathBuilder, createPlaceholderProxy, createTypedDoc, derivePlaceholder, deriveShapePlaceholder, evaluatePath, evaluatePathOnValue, getLoroContainer, getLoroDoc, hasWildcard, loro, mergeValue, overlayPlaceholder, validatePlaceholder };
|