@loro-extended/change 5.0.0 → 5.2.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 +94 -0
- package/dist/index.d.ts +262 -137
- package/dist/index.js +2027 -1930
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/fork-at.test.ts +260 -0
- package/src/functional-helpers.test.ts +402 -0
- package/src/functional-helpers.ts +172 -12
- package/src/index.ts +7 -2
- package/src/loro.ts +26 -2
- package/src/shape.ts +9 -5
- package/src/typed-doc.ts +58 -6
- package/src/typed-refs/base.ts +18 -0
- package/src/typed-refs/doc-ref-internals.ts +2 -2
- package/src/typed-refs/list-ref-base-internals.ts +2 -2
- package/src/typed-refs/list-ref-base.ts +2 -2
- package/src/typed-refs/record-ref-internals.ts +2 -2
- package/src/typed-refs/struct-ref-internals.ts +2 -2
- package/src/typed-refs/struct-ref.ts +7 -1
- package/src/typed-refs/tree-deleted-nodes.test.ts +213 -0
- package/src/typed-refs/tree-loro.test.ts +52 -0
- package/src/typed-refs/tree-node-ref-internals.ts +34 -1
- package/src/typed-refs/tree-node-ref.test.ts +24 -17
- package/src/typed-refs/tree-node-ref.ts +8 -0
- package/src/typed-refs/tree-node.test.ts +54 -22
- package/src/typed-refs/tree-ref.ts +10 -3
package/README.md
CHANGED
|
@@ -216,6 +216,100 @@ console.log(doc.toJSON()); // Updated document state
|
|
|
216
216
|
| Atomic undo/redo | Batched: `change(doc, d => { ... })` |
|
|
217
217
|
| Performance-critical bulk updates | Batched: `change(doc, d => { ... })` |
|
|
218
218
|
| Simple reads + writes | Direct: `doc.users.set(...)` |
|
|
219
|
+
| Encapsulated ref operations | Ref-level: `change(ref, d => {...})` |
|
|
220
|
+
|
|
221
|
+
### Ref-Level `change()` for Encapsulation
|
|
222
|
+
|
|
223
|
+
The `change()` function also works on individual refs (ListRef, TextRef, TreeRef, etc.), enabling better encapsulation when you want to pass refs around without exposing the entire document:
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { change } from "@loro-extended/change";
|
|
227
|
+
|
|
228
|
+
// Library code - expose only the ref, not the doc
|
|
229
|
+
class StateMachine {
|
|
230
|
+
private doc: TypedDoc<...>;
|
|
231
|
+
|
|
232
|
+
get states(): TreeRef<StateNodeShape> {
|
|
233
|
+
return this.doc.states;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// User code - works with just the ref
|
|
238
|
+
function addStates(states: TreeRef<StateNodeShape>) {
|
|
239
|
+
change(states, draft => {
|
|
240
|
+
const idle = draft.createNode();
|
|
241
|
+
idle.data.name.insert(0, "idle");
|
|
242
|
+
|
|
243
|
+
const running = draft.createNode();
|
|
244
|
+
running.data.name.insert(0, "running");
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Usage
|
|
249
|
+
const machine = new StateMachine();
|
|
250
|
+
addStates(machine.states); // No access to the underlying doc needed!
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
This pattern is useful for:
|
|
254
|
+
- **Library APIs**: Expose typed refs without leaking document structure
|
|
255
|
+
- **Component isolation**: Pass refs to components that only need partial access
|
|
256
|
+
- **Testing**: Mock or stub individual refs without full document setup
|
|
257
|
+
|
|
258
|
+
All ref types support `change()`:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// ListRef
|
|
262
|
+
change(doc.items, draft => {
|
|
263
|
+
draft.push("item1");
|
|
264
|
+
draft.push("item2");
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// TextRef
|
|
268
|
+
change(doc.title, draft => {
|
|
269
|
+
draft.insert(0, "Hello ");
|
|
270
|
+
draft.insert(6, "World");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// CounterRef
|
|
274
|
+
change(doc.count, draft => {
|
|
275
|
+
draft.increment(5);
|
|
276
|
+
draft.decrement(2);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// StructRef
|
|
280
|
+
change(doc.profile, draft => {
|
|
281
|
+
draft.bio.insert(0, "Hello");
|
|
282
|
+
draft.age.increment(1);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// RecordRef
|
|
286
|
+
change(doc.users, draft => {
|
|
287
|
+
draft.set("alice", { name: "Alice" });
|
|
288
|
+
draft.set("bob", { name: "Bob" });
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// TreeRef
|
|
292
|
+
change(doc.tree, draft => {
|
|
293
|
+
const node = draft.createNode();
|
|
294
|
+
node.data.name.insert(0, "root");
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Nested `change()` calls are safe - Loro's commit is idempotent:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
change(doc.items, outer => {
|
|
302
|
+
outer.push("from outer");
|
|
303
|
+
|
|
304
|
+
// Nested change on a different ref - works correctly
|
|
305
|
+
change(doc.count, inner => {
|
|
306
|
+
inner.increment(10);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
outer.push("still in outer");
|
|
310
|
+
});
|
|
311
|
+
// All mutations are committed
|
|
312
|
+
```
|
|
219
313
|
|
|
220
314
|
## Advanced Usage
|
|
221
315
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LoroDoc, Container, LoroTreeNode, TreeID, Subscription, LoroText, LoroList, LoroMovableList, LoroMap,
|
|
1
|
+
import { PeerID, LoroDoc, Container, LoroTreeNode, TreeID, Subscription, LoroText, LoroList, LoroMovableList, LoroMap, LoroTree, LoroCounter, Value } from 'loro-crdt';
|
|
2
2
|
|
|
3
3
|
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
4
4
|
/**
|
|
@@ -135,6 +135,14 @@ type JsonPatch = JsonPatchOperation[];
|
|
|
135
135
|
* loro(doc).subscribe(callback);
|
|
136
136
|
* ```
|
|
137
137
|
*/
|
|
138
|
+
/**
|
|
139
|
+
* Frontiers represent a specific version in the document's history.
|
|
140
|
+
* Each frontier is an operation ID consisting of a peer ID and counter.
|
|
141
|
+
*/
|
|
142
|
+
type Frontiers = {
|
|
143
|
+
peer: PeerID;
|
|
144
|
+
counter: number;
|
|
145
|
+
}[];
|
|
138
146
|
type TypedDoc<Shape extends DocShape> = Mutable<Shape> & {
|
|
139
147
|
/**
|
|
140
148
|
* The primary method of mutating typed documents.
|
|
@@ -168,6 +176,32 @@ type TypedDoc<Shape extends DocShape> = Mutable<Shape> & {
|
|
|
168
176
|
* ```
|
|
169
177
|
*/
|
|
170
178
|
toJSON(): Infer<Shape>;
|
|
179
|
+
/**
|
|
180
|
+
* Creates a new TypedDoc at a specified version (frontiers).
|
|
181
|
+
* The forked doc will only contain history before the specified frontiers.
|
|
182
|
+
* The forked doc has a different PeerID from the original.
|
|
183
|
+
*
|
|
184
|
+
* For raw LoroDoc access, use: `loro(doc).doc.forkAt(frontiers)`
|
|
185
|
+
*
|
|
186
|
+
* @param frontiers - The version to fork at (obtained from `loro(doc).doc.frontiers()`)
|
|
187
|
+
* @returns A new TypedDoc with the same schema at the specified version
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* import { loro } from "@loro-extended/change";
|
|
192
|
+
*
|
|
193
|
+
* const doc = createTypedDoc(schema);
|
|
194
|
+
* doc.title.update("Hello");
|
|
195
|
+
* const frontiers = loro(doc).doc.frontiers();
|
|
196
|
+
* doc.title.update("World");
|
|
197
|
+
*
|
|
198
|
+
* // Fork at the earlier version
|
|
199
|
+
* const forkedDoc = doc.forkAt(frontiers);
|
|
200
|
+
* console.log(forkedDoc.title.toString()); // "Hello"
|
|
201
|
+
* console.log(doc.title.toString()); // "World"
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
forkAt(frontiers: Frontiers): TypedDoc<Shape>;
|
|
171
205
|
};
|
|
172
206
|
/**
|
|
173
207
|
* Creates a new TypedDoc with the given schema.
|
|
@@ -207,6 +241,133 @@ type TypedDoc<Shape extends DocShape> = Mutable<Shape> & {
|
|
|
207
241
|
*/
|
|
208
242
|
declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDoc?: LoroDoc): TypedDoc<Shape>;
|
|
209
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Symbol for internal methods that should not be enumerable or accessible to users.
|
|
246
|
+
* Used to hide implementation details like absorbPlainValues(), getTypedRefParams(), etc.
|
|
247
|
+
*
|
|
248
|
+
* This achieves Success Criteria #7 from loro-api-refactor.md:
|
|
249
|
+
* "Internal methods hidden - Via Symbol, not enumerable"
|
|
250
|
+
*/
|
|
251
|
+
declare const INTERNAL_SYMBOL: unique symbol;
|
|
252
|
+
/**
|
|
253
|
+
* Minimal interface for refs that only need absorbPlainValues.
|
|
254
|
+
* Used by TreeNodeRef which doesn't extend TypedRef.
|
|
255
|
+
*/
|
|
256
|
+
interface RefInternalsBase {
|
|
257
|
+
/** Absorb mutated plain values back into Loro containers */
|
|
258
|
+
absorbPlainValues(): void;
|
|
259
|
+
}
|
|
260
|
+
type TypedRefParams<Shape extends DocShape | ContainerShape> = {
|
|
261
|
+
shape: Shape;
|
|
262
|
+
placeholder?: Infer<Shape>;
|
|
263
|
+
getContainer: () => ShapeToContainer<Shape>;
|
|
264
|
+
autoCommit?: boolean;
|
|
265
|
+
batchedMutation?: boolean;
|
|
266
|
+
getDoc: () => LoroDoc;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Abstract base class for all ref internal implementations.
|
|
270
|
+
* Contains shared logic that was previously in TypedRef.createBaseInternals().
|
|
271
|
+
*
|
|
272
|
+
* Subclasses implement specific behavior for each ref type.
|
|
273
|
+
*/
|
|
274
|
+
declare abstract class BaseRefInternals<Shape extends DocShape | ContainerShape> implements RefInternalsBase {
|
|
275
|
+
protected readonly params: TypedRefParams<Shape>;
|
|
276
|
+
protected cachedContainer: ShapeToContainer<Shape> | undefined;
|
|
277
|
+
protected loroNamespace: LoroRefBase | undefined;
|
|
278
|
+
constructor(params: TypedRefParams<Shape>);
|
|
279
|
+
/** Get the underlying Loro container (cached) */
|
|
280
|
+
getContainer(): ShapeToContainer<Shape>;
|
|
281
|
+
/** Commit changes if autoCommit is enabled */
|
|
282
|
+
commitIfAuto(): void;
|
|
283
|
+
/** Get the shape for this ref */
|
|
284
|
+
getShape(): Shape;
|
|
285
|
+
/** Get the placeholder value */
|
|
286
|
+
getPlaceholder(): Infer<Shape> | undefined;
|
|
287
|
+
/** Check if autoCommit is enabled */
|
|
288
|
+
getAutoCommit(): boolean;
|
|
289
|
+
/** Check if in batched mutation mode */
|
|
290
|
+
getBatchedMutation(): boolean;
|
|
291
|
+
/** Get the LoroDoc */
|
|
292
|
+
getDoc(): LoroDoc;
|
|
293
|
+
/**
|
|
294
|
+
* Get the TypedRefParams needed to recreate this ref.
|
|
295
|
+
* Used by change() to create draft refs with modified params.
|
|
296
|
+
*
|
|
297
|
+
* Returns a new params object with the same shape, placeholder, getContainer, and getDoc,
|
|
298
|
+
* but allows overriding autoCommit and batchedMutation for draft creation.
|
|
299
|
+
*/
|
|
300
|
+
getTypedRefParams(): TypedRefParams<Shape>;
|
|
301
|
+
/** Get the loro namespace (cached) */
|
|
302
|
+
getLoroNamespace(): LoroRefBase;
|
|
303
|
+
/** Absorb mutated plain values back into Loro containers - subclasses override */
|
|
304
|
+
abstract absorbPlainValues(): void;
|
|
305
|
+
/** Create the loro() namespace object - subclasses override for specific types */
|
|
306
|
+
protected createLoroNamespace(): LoroRefBase;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Base class for all typed refs.
|
|
310
|
+
*
|
|
311
|
+
* All internal methods are accessed via [INTERNAL_SYMBOL] to prevent
|
|
312
|
+
* namespace collisions with user data properties.
|
|
313
|
+
*
|
|
314
|
+
* Uses the Facade + Implementation pattern:
|
|
315
|
+
* - TypedRef is the thin public facade
|
|
316
|
+
* - BaseRefInternals subclasses contain all implementation logic
|
|
317
|
+
*/
|
|
318
|
+
declare abstract class TypedRef<Shape extends DocShape | ContainerShape> {
|
|
319
|
+
/**
|
|
320
|
+
* Internal implementation accessed via Symbol.
|
|
321
|
+
* Subclasses must set this to their specific internals class instance.
|
|
322
|
+
*/
|
|
323
|
+
abstract [INTERNAL_SYMBOL]: BaseRefInternals<Shape>;
|
|
324
|
+
/**
|
|
325
|
+
* Serializes the ref to a plain JSON-compatible value.
|
|
326
|
+
* Returns the plain type inferred from the shape.
|
|
327
|
+
*/
|
|
328
|
+
abstract toJSON(): Infer<Shape>;
|
|
329
|
+
/**
|
|
330
|
+
* Access the loro() namespace via the well-known symbol.
|
|
331
|
+
* This is used by the loro() function to access CRDT internals.
|
|
332
|
+
*/
|
|
333
|
+
get [LORO_SYMBOL](): LoroRefBase;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Internal implementation for CounterRef.
|
|
338
|
+
* Contains all logic, state, and implementation details.
|
|
339
|
+
*/
|
|
340
|
+
declare class CounterRefInternals extends BaseRefInternals<CounterContainerShape> {
|
|
341
|
+
private materialized;
|
|
342
|
+
/** Increment the counter value */
|
|
343
|
+
increment(value?: number): void;
|
|
344
|
+
/** Decrement the counter value */
|
|
345
|
+
decrement(value?: number): void;
|
|
346
|
+
/** Get the current counter value */
|
|
347
|
+
getValue(): number;
|
|
348
|
+
/** No plain values in counter */
|
|
349
|
+
absorbPlainValues(): void;
|
|
350
|
+
/** Create the loro namespace for counter */
|
|
351
|
+
protected createLoroNamespace(): LoroCounterRef;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Counter typed ref - thin facade that delegates to CounterRefInternals.
|
|
356
|
+
*/
|
|
357
|
+
declare class CounterRef extends TypedRef<CounterContainerShape> {
|
|
358
|
+
[INTERNAL_SYMBOL]: CounterRefInternals;
|
|
359
|
+
constructor(params: TypedRefParams<CounterContainerShape>);
|
|
360
|
+
/** Increment the counter by the given value (default 1) */
|
|
361
|
+
increment(value?: number): void;
|
|
362
|
+
/** Decrement the counter by the given value (default 1) */
|
|
363
|
+
decrement(value?: number): void;
|
|
364
|
+
/** Get the current counter value */
|
|
365
|
+
get value(): number;
|
|
366
|
+
valueOf(): number;
|
|
367
|
+
toJSON(): number;
|
|
368
|
+
[Symbol.toPrimitive](hint: string): number | string;
|
|
369
|
+
}
|
|
370
|
+
|
|
210
371
|
/**
|
|
211
372
|
* Internal implementation for ListRefBase.
|
|
212
373
|
* Contains all logic, state, and implementation details for list operations.
|
|
@@ -214,7 +375,7 @@ declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDo
|
|
|
214
375
|
declare class ListRefBaseInternals<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"], MutableItem = NestedShape["_mutable"]> extends BaseRefInternals<any> {
|
|
215
376
|
private itemCache;
|
|
216
377
|
/** Get typed ref params for creating child refs at an index */
|
|
217
|
-
|
|
378
|
+
getChildTypedRefParams(index: number, shape: ContainerShape): TypedRefParams<ContainerShape>;
|
|
218
379
|
/** Get item for predicate functions (returns plain value) */
|
|
219
380
|
getPredicateItem(index: number): Item | undefined;
|
|
220
381
|
/** Get mutable item for return values (returns ref or cached value) */
|
|
@@ -310,7 +471,7 @@ declare class MovableListRef<NestedShape extends ContainerOrValueShape, Item = N
|
|
|
310
471
|
declare class RecordRefInternals<NestedShape extends ContainerOrValueShape> extends BaseRefInternals<any> {
|
|
311
472
|
private refCache;
|
|
312
473
|
/** Get typed ref params for creating child refs at a key */
|
|
313
|
-
|
|
474
|
+
getChildTypedRefParams(key: string, shape: ContainerShape): TypedRefParams<ContainerShape>;
|
|
314
475
|
/** Get a ref for a key without creating (returns undefined for non-existent container keys) */
|
|
315
476
|
getRef(key: string): unknown;
|
|
316
477
|
/** Get or create a ref for a key (always creates for container shapes) */
|
|
@@ -398,6 +559,11 @@ type StructRef<NestedShapes extends Record<string, ContainerOrValueShape>> = {
|
|
|
398
559
|
* @internal
|
|
399
560
|
*/
|
|
400
561
|
[INTERNAL_SYMBOL]: RefInternalsBase;
|
|
562
|
+
/**
|
|
563
|
+
* Access CRDT internals via the well-known symbol.
|
|
564
|
+
* Used by the loro() function.
|
|
565
|
+
*/
|
|
566
|
+
[LORO_SYMBOL]: LoroMapRef;
|
|
401
567
|
};
|
|
402
568
|
|
|
403
569
|
/**
|
|
@@ -481,6 +647,7 @@ interface TreeRefLike<DataShape extends StructContainerShape> {
|
|
|
481
647
|
declare class TreeNodeRefInternals<DataShape extends StructContainerShape> implements RefInternalsBase {
|
|
482
648
|
private readonly params;
|
|
483
649
|
private dataRef;
|
|
650
|
+
private loroNamespace;
|
|
484
651
|
constructor(params: TreeNodeRefParams<DataShape>);
|
|
485
652
|
/** Get the underlying LoroTreeNode */
|
|
486
653
|
getNode(): LoroTreeNode;
|
|
@@ -500,6 +667,10 @@ declare class TreeNodeRefInternals<DataShape extends StructContainerShape> imple
|
|
|
500
667
|
getOrCreateDataRef(): StructRef<DataShape["shapes"]>;
|
|
501
668
|
/** Absorb mutated plain values back into Loro containers */
|
|
502
669
|
absorbPlainValues(): void;
|
|
670
|
+
/** Get the loro namespace (cached) */
|
|
671
|
+
getLoroNamespace(): LoroTreeNodeRef;
|
|
672
|
+
/** Create the loro namespace for tree node */
|
|
673
|
+
protected createLoroNamespace(): LoroTreeNodeRef;
|
|
503
674
|
}
|
|
504
675
|
|
|
505
676
|
interface TreeNodeRefParams<DataShape extends StructContainerShape> {
|
|
@@ -529,6 +700,10 @@ interface TreeNodeRefParams<DataShape extends StructContainerShape> {
|
|
|
529
700
|
declare class TreeNodeRef<DataShape extends StructContainerShape> {
|
|
530
701
|
[INTERNAL_SYMBOL]: TreeNodeRefInternals<DataShape>;
|
|
531
702
|
constructor(params: TreeNodeRefParams<DataShape>);
|
|
703
|
+
/**
|
|
704
|
+
* Access the loro() namespace via the well-known symbol.
|
|
705
|
+
*/
|
|
706
|
+
get [LORO_SYMBOL](): LoroTreeNodeRef;
|
|
532
707
|
/**
|
|
533
708
|
* The unique TreeID of this node.
|
|
534
709
|
*/
|
|
@@ -675,9 +850,14 @@ declare class TreeRef<DataShape extends StructContainerShape> extends TypedRef<T
|
|
|
675
850
|
roots(): TreeNodeRef<DataShape>[];
|
|
676
851
|
/**
|
|
677
852
|
* Get all nodes in the tree (unordered).
|
|
678
|
-
*
|
|
853
|
+
* By default, excludes deleted nodes (tombstones).
|
|
854
|
+
*
|
|
855
|
+
* @param options.includeDeleted - If true, includes deleted nodes. Default: false.
|
|
856
|
+
* @returns Array of TreeNodeRef for matching nodes
|
|
679
857
|
*/
|
|
680
|
-
nodes(
|
|
858
|
+
nodes(options?: {
|
|
859
|
+
includeDeleted?: boolean;
|
|
860
|
+
}): TreeNodeRef<DataShape>[];
|
|
681
861
|
/**
|
|
682
862
|
* Check if a node with the given ID exists in the tree.
|
|
683
863
|
*/
|
|
@@ -804,6 +984,13 @@ interface LoroTreeRef extends LoroRefBase {
|
|
|
804
984
|
/** The underlying LoroTree */
|
|
805
985
|
readonly container: LoroTree;
|
|
806
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* loro() return type for TreeNodeRef.
|
|
989
|
+
*/
|
|
990
|
+
interface LoroTreeNodeRef extends LoroRefBase {
|
|
991
|
+
/** The underlying LoroTreeNode */
|
|
992
|
+
readonly container: LoroTreeNode;
|
|
993
|
+
}
|
|
807
994
|
/**
|
|
808
995
|
* loro() return type for TypedDoc.
|
|
809
996
|
* Provides access to doc-level operations.
|
|
@@ -849,7 +1036,11 @@ declare function loro(ref: CounterRef): LoroCounterRef;
|
|
|
849
1036
|
/**
|
|
850
1037
|
* Access CRDT internals for a TreeRef.
|
|
851
1038
|
*/
|
|
852
|
-
declare function loro<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>): LoroTreeRef;
|
|
1039
|
+
declare function loro<DataShape extends StructContainerShape>(ref: TreeRef<DataShape> | TreeRefInterface<DataShape>): LoroTreeRef;
|
|
1040
|
+
/**
|
|
1041
|
+
* Access CRDT internals for a TreeNodeRef.
|
|
1042
|
+
*/
|
|
1043
|
+
declare function loro<DataShape extends StructContainerShape>(ref: TreeNodeRef<DataShape>): LoroTreeNodeRef;
|
|
853
1044
|
/**
|
|
854
1045
|
* Access CRDT internals for a TypedDoc.
|
|
855
1046
|
*/
|
|
@@ -859,125 +1050,6 @@ declare function loro<Shape extends DocShape>(doc: TypedDoc<Shape>): LoroTypedDo
|
|
|
859
1050
|
*/
|
|
860
1051
|
declare function loro<Shape extends ContainerShape>(ref: TypedRef<Shape>): LoroRefBase;
|
|
861
1052
|
|
|
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 */
|
|
876
|
-
absorbPlainValues(): void;
|
|
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> {
|
|
929
|
-
/**
|
|
930
|
-
* Internal implementation accessed via Symbol.
|
|
931
|
-
* Subclasses must set this to their specific internals class instance.
|
|
932
|
-
*/
|
|
933
|
-
abstract [INTERNAL_SYMBOL]: BaseRefInternals<Shape>;
|
|
934
|
-
/**
|
|
935
|
-
* Serializes the ref to a plain JSON-compatible value.
|
|
936
|
-
* Returns the plain type inferred from the shape.
|
|
937
|
-
*/
|
|
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;
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
/**
|
|
947
|
-
* Internal implementation for CounterRef.
|
|
948
|
-
* Contains all logic, state, and implementation details.
|
|
949
|
-
*/
|
|
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 */
|
|
959
|
-
absorbPlainValues(): void;
|
|
960
|
-
/** Create the loro namespace for counter */
|
|
961
|
-
protected createLoroNamespace(): LoroCounterRef;
|
|
962
|
-
}
|
|
963
|
-
|
|
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;
|
|
979
|
-
}
|
|
980
|
-
|
|
981
1053
|
type WithPlaceholder<S extends Shape<any, any, any>> = S & {
|
|
982
1054
|
placeholder(value: S["_placeholder"]): S;
|
|
983
1055
|
};
|
|
@@ -1034,8 +1106,10 @@ interface TreeRefInterface<DataShape extends StructContainerShape> {
|
|
|
1034
1106
|
createNode(initialData?: Partial<DataShape["_plain"]>): TreeNodeRef<DataShape>;
|
|
1035
1107
|
/** Get all root nodes (nodes without parents) */
|
|
1036
1108
|
roots(): TreeNodeRef<DataShape>[];
|
|
1037
|
-
/** Get all nodes in the tree (unordered) */
|
|
1038
|
-
nodes(
|
|
1109
|
+
/** Get all nodes in the tree (unordered). By default excludes deleted nodes. */
|
|
1110
|
+
nodes(options?: {
|
|
1111
|
+
includeDeleted?: boolean;
|
|
1112
|
+
}): TreeNodeRef<DataShape>[];
|
|
1039
1113
|
/** Check if a node with the given ID exists in the tree */
|
|
1040
1114
|
has(id: TreeID): boolean;
|
|
1041
1115
|
/** Enable fractional index generation for ordering */
|
|
@@ -1048,6 +1122,10 @@ interface TreeRefInterface<DataShape extends StructContainerShape> {
|
|
|
1048
1122
|
fractionalIndex: string;
|
|
1049
1123
|
data: DataShape["_plain"];
|
|
1050
1124
|
}>;
|
|
1125
|
+
/**
|
|
1126
|
+
* Access CRDT internals via the well-known symbol.
|
|
1127
|
+
*/
|
|
1128
|
+
readonly [LORO_SYMBOL]: LoroTreeRef;
|
|
1051
1129
|
}
|
|
1052
1130
|
/**
|
|
1053
1131
|
* Container shape for tree (forest) structures.
|
|
@@ -1092,9 +1170,7 @@ type MapContainerShape<NestedShapes extends Record<string, ContainerOrValueShape
|
|
|
1092
1170
|
*/
|
|
1093
1171
|
interface StructContainerShape<NestedShapes extends Record<string, ContainerOrValueShape> = Record<string, ContainerOrValueShape>> extends Shape<{
|
|
1094
1172
|
[K in keyof NestedShapes]: NestedShapes[K]["_plain"];
|
|
1095
|
-
}, StructRef<NestedShapes
|
|
1096
|
-
[K in keyof NestedShapes]: NestedShapes[K]["_mutable"];
|
|
1097
|
-
}, {
|
|
1173
|
+
}, StructRef<NestedShapes>, {
|
|
1098
1174
|
[K in keyof NestedShapes]: NestedShapes[K]["_placeholder"];
|
|
1099
1175
|
}> {
|
|
1100
1176
|
readonly _type: "struct";
|
|
@@ -1394,7 +1470,7 @@ declare function derivePlaceholder<T extends DocShape>(schema: T): InferPlacehol
|
|
|
1394
1470
|
declare function deriveShapePlaceholder(shape: ContainerOrValueShape): unknown;
|
|
1395
1471
|
|
|
1396
1472
|
/**
|
|
1397
|
-
* The primary method of mutating typed documents.
|
|
1473
|
+
* The primary method of mutating typed documents and refs.
|
|
1398
1474
|
* Batches multiple mutations into a single transaction.
|
|
1399
1475
|
* All changes commit together at the end.
|
|
1400
1476
|
*
|
|
@@ -1403,26 +1479,46 @@ declare function deriveShapePlaceholder(shape: ContainerOrValueShape): unknown;
|
|
|
1403
1479
|
* - Performance (fewer commits)
|
|
1404
1480
|
* - Atomic undo (all changes = one undo step)
|
|
1405
1481
|
*
|
|
1406
|
-
* Returns the doc for chaining.
|
|
1482
|
+
* Returns the doc/ref for chaining.
|
|
1407
1483
|
*
|
|
1408
|
-
* @param
|
|
1484
|
+
* @param target - The TypedDoc or TypedRef to mutate
|
|
1409
1485
|
* @param fn - Function that performs mutations on the draft
|
|
1410
|
-
* @returns The same
|
|
1486
|
+
* @returns The same target for chaining
|
|
1411
1487
|
*
|
|
1412
1488
|
* @example
|
|
1413
1489
|
* ```typescript
|
|
1414
1490
|
* import { change } from "@loro-extended/change"
|
|
1415
1491
|
*
|
|
1416
|
-
* //
|
|
1492
|
+
* // Document-level change (chainable)
|
|
1417
1493
|
* change(doc, draft => {
|
|
1418
1494
|
* draft.count.increment(10)
|
|
1419
1495
|
* draft.title.update("Hello")
|
|
1420
1496
|
* })
|
|
1421
1497
|
* .count.increment(5) // Optional: continue mutating
|
|
1422
|
-
* .toJSON() // Optional: get
|
|
1498
|
+
* .toJSON() // Optional: get snapshot
|
|
1499
|
+
*
|
|
1500
|
+
* // Ref-level change - enables encapsulation
|
|
1501
|
+
* function addItems(list: ListRef<...>) {
|
|
1502
|
+
* change(list, draft => {
|
|
1503
|
+
* draft.push({ name: "item1" })
|
|
1504
|
+
* draft.push({ name: "item2" })
|
|
1505
|
+
* })
|
|
1506
|
+
* }
|
|
1507
|
+
*
|
|
1508
|
+
* // TreeRef example - pass around refs without exposing the doc
|
|
1509
|
+
* function addStates(states: TreeRef<StateShape>) {
|
|
1510
|
+
* change(states, draft => {
|
|
1511
|
+
* draft.createNode({ name: "idle" })
|
|
1512
|
+
* draft.createNode({ name: "running" })
|
|
1513
|
+
* })
|
|
1514
|
+
* }
|
|
1423
1515
|
* ```
|
|
1424
1516
|
*/
|
|
1425
1517
|
declare function change<Shape extends DocShape>(doc: TypedDoc<Shape>, fn: (draft: Mutable<Shape>) => void): TypedDoc<Shape>;
|
|
1518
|
+
declare function change<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>, fn: (draft: TreeRef<DataShape>) => void): TreeRef<DataShape>;
|
|
1519
|
+
declare function change<DataShape extends StructContainerShape>(ref: TreeRefInterface<DataShape>, fn: (draft: TreeRefInterface<DataShape>) => void): TreeRefInterface<DataShape>;
|
|
1520
|
+
declare function change<NestedShapes extends Record<string, ContainerOrValueShape>>(ref: StructRef<NestedShapes>, fn: (draft: StructRef<NestedShapes>) => void): StructRef<NestedShapes>;
|
|
1521
|
+
declare function change<T extends TypedRef<ContainerShape>>(ref: T, fn: (draft: T) => void): T;
|
|
1426
1522
|
/**
|
|
1427
1523
|
* Access the underlying LoroDoc for advanced operations.
|
|
1428
1524
|
* Works on both TypedDoc and any typed ref (TextRef, CounterRef, ListRef, etc.).
|
|
@@ -1448,6 +1544,7 @@ declare function change<Shape extends DocShape>(doc: TypedDoc<Shape>, fn: (draft
|
|
|
1448
1544
|
declare function getLoroDoc<Shape extends DocShape>(doc: TypedDoc<Shape>): LoroDoc;
|
|
1449
1545
|
declare function getLoroDoc<Shape extends ContainerShape>(ref: TypedRef<Shape>): LoroDoc;
|
|
1450
1546
|
declare function getLoroDoc<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>): LoroDoc;
|
|
1547
|
+
declare function getLoroDoc<DataShape extends StructContainerShape>(ref: TreeRefInterface<DataShape>): LoroDoc;
|
|
1451
1548
|
/**
|
|
1452
1549
|
* Access the underlying Loro container from a typed ref.
|
|
1453
1550
|
* Returns the correctly-typed container based on the ref type.
|
|
@@ -1480,7 +1577,35 @@ declare function getLoroContainer(ref: TypedRef<RecordContainerShape>): LoroMap;
|
|
|
1480
1577
|
declare function getLoroContainer(ref: TypedRef<StructContainerShape>): LoroMap;
|
|
1481
1578
|
declare function getLoroContainer<NestedShapes extends Record<string, ContainerOrValueShape>>(ref: StructRef<NestedShapes>): LoroMap;
|
|
1482
1579
|
declare function getLoroContainer<DataShape extends StructContainerShape>(ref: TreeRef<DataShape>): LoroTree;
|
|
1580
|
+
declare function getLoroContainer<DataShape extends StructContainerShape>(ref: TreeRefInterface<DataShape>): LoroTree;
|
|
1483
1581
|
declare function getLoroContainer<Shape extends ContainerShape>(ref: TypedRef<Shape>): ShapeToContainer<Shape>;
|
|
1582
|
+
/**
|
|
1583
|
+
* Creates a new TypedDoc at a specified version (frontiers).
|
|
1584
|
+
* The forked doc will only contain history before the specified frontiers.
|
|
1585
|
+
* The forked doc has a different PeerID from the original.
|
|
1586
|
+
*
|
|
1587
|
+
* For raw LoroDoc access, use: `loro(doc).doc.forkAt(frontiers)`
|
|
1588
|
+
*
|
|
1589
|
+
* @param doc - The TypedDoc to fork
|
|
1590
|
+
* @param frontiers - The version to fork at (obtained from `loro(doc).doc.frontiers()`)
|
|
1591
|
+
* @returns A new TypedDoc with the same schema at the specified version
|
|
1592
|
+
*
|
|
1593
|
+
* @example
|
|
1594
|
+
* ```typescript
|
|
1595
|
+
* import { forkAt, loro } from "@loro-extended/change"
|
|
1596
|
+
*
|
|
1597
|
+
* const doc = createTypedDoc(schema);
|
|
1598
|
+
* doc.title.update("Hello");
|
|
1599
|
+
* const frontiers = loro(doc).doc.frontiers();
|
|
1600
|
+
* doc.title.update("World");
|
|
1601
|
+
*
|
|
1602
|
+
* // Fork at the earlier version
|
|
1603
|
+
* const forkedDoc = forkAt(doc, frontiers);
|
|
1604
|
+
* console.log(forkedDoc.title.toString()); // "Hello"
|
|
1605
|
+
* console.log(doc.title.toString()); // "World"
|
|
1606
|
+
* ```
|
|
1607
|
+
*/
|
|
1608
|
+
declare function forkAt<Shape extends DocShape>(doc: TypedDoc<Shape>, frontiers: Frontiers): TypedDoc<Shape>;
|
|
1484
1609
|
|
|
1485
1610
|
/**
|
|
1486
1611
|
* Overlays CRDT state with placeholder defaults
|
|
@@ -1623,4 +1748,4 @@ declare function createPlaceholderProxy<T extends object>(target: T): T;
|
|
|
1623
1748
|
*/
|
|
1624
1749
|
declare function validatePlaceholder<T extends DocShape>(placeholder: unknown, schema: T): Infer<T>;
|
|
1625
1750
|
|
|
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 };
|
|
1751
|
+
export { type AnyContainerShape, type AnyValueShape, type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, CounterRef, type DiscriminatedUnionValueShape, type DocShape, type Frontiers, 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, forkAt, getLoroContainer, getLoroDoc, hasWildcard, loro, mergeValue, overlayPlaceholder, validatePlaceholder };
|